From 1f9bc46a81b7d8b191db4bcd82f3da2c4283d77f Mon Sep 17 00:00:00 2001 From: Michal Kolodziej Date: Fri, 26 Apr 2013 15:48:19 +0200 Subject: [PATCH] Encryption to a subkey from a GPG generated message is working. Still no decryption and keys are transmitted in plaintext! --- resources/openpgp.js | 715 ++---------------- resources/openpgp.min.js | 30 +- .../{openpgp.packet.marker.js => marker.js} | 30 +- .../openpgp.packet.encryptedsessionkey.js | 226 ------ src/packet/openpgp.packet.userid.js | 356 --------- src/packet/public_key.js | 36 +- .../public_key_encrypted_session_key.js | 3 +- src/packet/secret_key.js | 8 + src/packet/userid.js | 56 ++ test/general/packet.js | 61 +- 10 files changed, 218 insertions(+), 1303 deletions(-) rename src/packet/{openpgp.packet.marker.js => marker.js} (74%) delete mode 100644 src/packet/openpgp.packet.encryptedsessionkey.js delete mode 100644 src/packet/openpgp.packet.userid.js create mode 100644 src/packet/userid.js diff --git a/resources/openpgp.js b/resources/openpgp.js index 6ee0cf0a..c4d4c62c 100644 --- a/resources/openpgp.js +++ b/resources/openpgp.js @@ -7449,7 +7449,7 @@ function openpgp_config() { keyserver: "keyserver.linux.it" // "pgp.mit.edu:11371" }; - this.versionstring ="OpenPGP.js v.1.20130425"; + this.versionstring ="OpenPGP.js v.1.20130426"; this.commentstring ="http://openpgpjs.org"; /** * Reads the config out of the HTML5 local storage @@ -9677,232 +9677,6 @@ openpgp_packet_literal.format = { // 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 Public-Key Encrypted Session Key Packets (Tag 1) - * - * RFC4880 5.1: A Public-Key Encrypted Session Key packet holds the session key - * used to encrypt a message. Zero or more Public-Key Encrypted Session Key - * packets and/or Symmetric-Key Encrypted Session Key packets may precede a - * Symmetrically Encrypted Data Packet, which holds an encrypted message. The - * message is encrypted with the session key, and the session key is itself - * encrypted and stored in the Encrypted Session Key packet(s). The - * Symmetrically Encrypted Data Packet is preceded by one Public-Key Encrypted - * Session Key packet for each OpenPGP key to which the message is encrypted. - * The recipient of the message finds a session key that is encrypted to their - * public key, decrypts the session key, and then uses the session key to - * decrypt the message. - */ -function openpgp_packet_encryptedsessionkey() { - - /** - * Parsing function for a publickey encrypted session key packet (tag 1). - * - * @param {String} input Payload of a tag 1 packet - * @param {Integer} position Position to start reading from the input string - * @param {Integer} len Length of the packet or the remaining length of - * input at position - * @return {openpgp_packet_encrypteddata} Object representation - */ - function read_pub_key_packet(input, position, len) { - this.tagType = 1; - this.packetLength = len; - var mypos = position; - if (len < 10) { - util - .print_error("openpgp.packet.encryptedsessionkey.js\n" + 'invalid length'); - return null; - } - - this.version = input[mypos++].charCodeAt(); - this.keyId = new openpgp_type_keyid(); - this.keyId.read_packet(input, mypos); - mypos += 8; - this.publicKeyAlgorithmUsed = input[mypos++].charCodeAt(); - - switch (this.publicKeyAlgorithmUsed) { - case 1: - case 2: // RSA - this.MPIs = new Array(); - this.MPIs[0] = new openpgp_type_mpi(); - this.MPIs[0].read(input, mypos, mypos - position); - break; - case 16: // Elgamal - this.MPIs = new Array(); - this.MPIs[0] = new openpgp_type_mpi(); - this.MPIs[0].read(input, mypos, mypos - position); - mypos += this.MPIs[0].packetLength; - this.MPIs[1] = new openpgp_type_mpi(); - this.MPIs[1].read(input, mypos, mypos - position); - break; - default: - util.print_error("openpgp.packet.encryptedsessionkey.js\n" - + "unknown public key packet algorithm type " - + this.publicKeyAlgorithmType); - break; - } - return this; - } - - /** - * Create a string representation of a tag 1 packet - * - * @param {String} publicKeyId - * The public key id corresponding to publicMPIs key as string - * @param {openpgp_type_mpi[]} publicMPIs - * Multiprecision integer objects describing the public key - * @param {Integer} pubalgo - * The corresponding public key algorithm // See RFC4880 9.1 - * @param {Integer} symmalgo - * The symmetric cipher algorithm used to encrypt the data - * within an encrypteddatapacket or encryptedintegrity- - * protecteddatapacket - * following this packet //See RFC4880 9.2 - * @param {String} sessionkey - * A string of randombytes representing the session key - * @return {String} The string representation - */ - function write_pub_key_packet(publicKeyId, publicMPIs, pubalgo, symmalgo, - sessionkey) { - var result = String.fromCharCode(3); - var data = String.fromCharCode(symmalgo); - data += sessionkey; - var checksum = util.calc_checksum(sessionkey); - data += String.fromCharCode((checksum >> 8) & 0xFF); - data += String.fromCharCode((checksum) & 0xFF); - result += publicKeyId; - result += String.fromCharCode(pubalgo); - var mpi = new openpgp_type_mpi(); - var mpiresult = openpgp_crypto_asymetricEncrypt(pubalgo, publicMPIs, - mpi.create(openpgp_encoding_eme_pkcs1_encode(data, - publicMPIs[0].mpiByteLength))); - for ( var i = 0; i < mpiresult.length; i++) { - result += mpiresult[i]; - } - result = openpgp_packet.write_packet_header(1, result.length) + result; - return result; - } - - /** - * Parsing function for a symmetric encrypted session key packet (tag 3). - * - * @param {String} input Payload of a tag 1 packet - * @param {Integer} position Position to start reading from the input string - * @param {Integer} len - * Length of the packet or the remaining length of - * input at position - * @return {openpgp_packet_encrypteddata} Object representation - */ - function read_symmetric_key_packet(input, position, len) { - this.tagType = 3; - var mypos = position; - // A one-octet version number. The only currently defined version is 4. - this.version = input[mypos++]; - - // A one-octet number describing the symmetric algorithm used. - this.symmetricKeyAlgorithmUsed = input[mypos++]; - // A string-to-key (S2K) specifier, length as defined above. - this.s2k = new openpgp_type_s2k(); - this.s2k.read(input, mypos); - - // Optionally, the encrypted session key itself, which is decrypted - // with the string-to-key object. - if ((s2k.s2kLength + mypos) < len) { - this.encryptedSessionKey = new Array(); - for ( var i = (mypos - position); i < len; i++) { - this.encryptedSessionKey[i] = input[mypos++]; - } - } - return this; - } - /** - * Decrypts the session key (only for public key encrypted session key - * packets (tag 1) - * - * @param {openpgp_msg_message} msg - * The message object (with member encryptedData - * @param {openpgp_msg_privatekey} key - * Private key with secMPIs unlocked - * @return {String} The unencrypted session key - */ - function decrypt(msg, key) { - if (this.tagType == 1) { - var result = openpgp_crypto_asymetricDecrypt( - this.publicKeyAlgorithmUsed, key.publicKey.MPIs, - key.secMPIs, this.MPIs).toMPI(); - var checksum = ((result.charCodeAt(result.length - 2) << 8) + result - .charCodeAt(result.length - 1)); - var decoded = openpgp_encoding_eme_pkcs1_decode(result.substring(2, result.length - 2), key.publicKey.MPIs[0].getByteLength()); - var sesskey = decoded.substring(1); - var algo = decoded.charCodeAt(0); - if (msg.encryptedData.tagType == 18) - return msg.encryptedData.decrypt(algo, sesskey); - else - return msg.encryptedData.decrypt_sym(algo, sesskey); - } else if (this.tagType == 3) { - util - .print_error("Symmetric encrypted sessionkey is not supported!"); - return null; - } - } - - /** - * Creates a string representation of this object (useful for debug - * purposes) - * - * @return {String} The string containing a openpgp description - */ - function toString() { - if (this.tagType == 1) { - var result = '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'; - for ( var i = 0; i < this.MPIs.length; i++) { - result += this.MPIs[i].toString(); - } - return result; - } else - return '5.3 Symmetric-Key Encrypted Session Key Packets (Tag 3)\n' - + ' KeyId: ' + this.keyId.toString() + '\n' - + ' length: ' + this.packetLength + '\n' - + ' version:' + this.version + '\n' + ' symKeyA:' - + this.symmetricKeyAlgorithmUsed + '\n' + ' s2k: ' - + this.s2k + '\n'; - } - - this.read_pub_key_packet = read_pub_key_packet; - this.read_symmetric_key_packet = read_symmetric_key_packet; - this.write_pub_key_packet = write_pub_key_packet; - this.toString = toString; - this.decrypt = decrypt; -}; - -// 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) @@ -9915,7 +9689,8 @@ function openpgp_packet_encryptedsessionkey() { * Such a packet MUST be ignored when received. */ function openpgp_packet_marker() { - this.tagType = 10; + this.tag = 10; + /** * Parsing function for a literal data packet (tag 10). * @@ -9927,29 +9702,14 @@ function openpgp_packet_marker() { * input at position * @return {openpgp_packet_encrypteddata} Object representation */ - function read_packet(input, position, len) { - this.packetLength = 3; - if (input[position].charCodeAt() == 0x50 && // P - input[position + 1].charCodeAt() == 0x47 && // G - input[position + 2].charCodeAt() == 0x50) // P - return this; + this.read = function(bytes) { + if (bytes[0].charCodeAt() == 0x50 && // P + bytes[1].charCodeAt() == 0x47 && // G + bytes[2].charCodeAt() == 0x50) // P + return true; // marker packet does not contain "PGP" - return null; + return false; } - - /** - * Generates Debug output - * - * @return {String} String which gives some information about the - * keymaterial - */ - function toString() { - return "5.8. Marker Packet (Obsolete Literal Packet) (Tag 10)\n" - + " packet reads: \"PGP\"\n"; - } - - this.read_packet = read_packet; - this.toString = toString; } // GPG4Browsers - An OpenPGP implementation in javascript // Copyright (C) 2011 Recurity Labs GmbH @@ -10969,362 +10729,6 @@ function openpgp_packet_userattribute() { // 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 User ID Packet (Tag 13) - * A User ID packet consists of UTF-8 text that is intended to represent - * the name and email address of the key holder. By convention, it - * includes an RFC 2822 [RFC2822] mail name-addr, but there are no - * restrictions on its content. The packet length in the header - * specifies the length of the User ID. - */ - -function openpgp_packet_userid() { - this.text = '' - this.tagType = 13; - this.certificationSignatures = new Array(); - this.certificationRevocationSignatures = new Array(); - this.revocationSignatures = new Array(); - this.parentNode = null; - - /** - * Set the packet text field to a native javascript string - * Conversion to a proper utf8 encoding takes place when the - * packet is written. - * @param {String} str Any native javascript string - */ - this.set_text = function(str) { - this.text = str; - } - - /** - * Set the packet text to value represented by the provided string - * of bytes. - * @param {String} bytes A string of bytes - */ - this.set_text_bytes = function(bytes) { - this.text = util.decode_utf8(bytes); - } - - /** - * Get the byte sequence representing the text of this packet. - * @returns {String} A sequence of bytes - */ - this.get_text_bytes = function() { - return util.encode_utf8(this.text); - } - - - /** - * Parsing function for a user id packet (tag 13). - * @param {String} input payload of a tag 13 packet - * @param {Integer} position position to start reading from the input string - * @param {Integer} len length of the packet or the remaining length of input at position - * @return {openpgp_packet_encrypteddata} object representation - */ - this.read = function(bytes) { - this.set_text_bytes(bytes); - return this; - } - - this.read_packet = function(){}; - - /** - * Creates a string representation of the user id packet - * @param {String} user_id the user id as string ("John Doe 15 - && result.signatureType < 20) { // certification - // // - // signature - this.certificationSignatures[this.certificationSignatures.length] = result; - break; - } else if (result.signatureType == 48) {// certification revocation signature - this.certificationRevocationSignatures[this.certificationRevocationSignatures.length] = result; - break; - } else if (result.signatureType == 24) { // omg. standalone signature - this.certificationSignatures[this.certificationSignatures.length] = result; - break; - } else { - util.print_debug("unknown sig t: "+result.signatureType+"@"+(pos - (result.packetLength + result.headerLength))); - } - default: - this.data = input; - this.position = position - parent_node.packetLength; - this.len = pos - position -(result.headerLength + result.packetLength); - return this.len; - } - } - } - this.data = input; - this.position = position - parent_node.packetLength; - this.len = pos - position -(result.headerLength + result.packetLength); - return this.len; - } else if (parent_node.tagType == 5) { // secret Key - this.parentNode = parent_node; - var exit = false; - var pos = position; - while (input.length != pos) { - var result = openpgp_packet.read_packet(input, pos, l - (pos - position)); - if (result == null) { - util.print_error('parsing ends here @:' + pos + " l:" + l); - break; - } else { - pos += result.packetLength + result.headerLength; - l = input.length - pos; - switch (result.tagType) { - case 2: // Signature Packet certification signature - if (result.signatureType > 15 - && result.signatureType < 20) - this.certificationSignatures[this.certificationSignatures.length] = result; - // certification revocation signature - else if (result.signatureType == 48) - this.certificationRevocationSignatures[this.certificationRevocationSignatures.length] = result; - default: - this.data = input; - this.position = position - parent_node.packetLength; - this.len = pos - position -(result.headerLength + result.packetLength); - return this.len; - } - } - } - } else { - util.print_error("unknown parent node for a userId packet "+parent_node.tagType); - } - } - - /** - * generates debug output (pretty print) - * @return {String} String which gives some information about the user id packet - */ - this.toString = function() { - var result = ' 5.11. User ID Packet (Tag 13)\n' + ' text (' - + this.text.length + '): "' + this.text.replace("<", "<") - + '"\n'; - result +="certification signatures:\n"; - for (var i = 0; i < this.certificationSignatures.length; i++) { - result += " "+this.certificationSignatures[i].toString(); - } - result +="certification revocation signatures:\n"; - for (var i = 0; i < this.certificationRevocationSignatures.length; i++) { - result += " "+this.certificationRevocationSignatures[i].toString(); - } - return result; - } - - /** - * lookup function to find certification revocation signatures - * @param {String} keyId string containing the key id of the issuer of this signature - * @return a CertificationRevocationSignature if found; otherwise null - */ - this.hasCertificationRevocationSignature = function(keyId) { - for (var i = 0; i < this.certificationRevocationSignatures.length; i++) { - if ((this.certificationRevocationSignatures[i].version == 3 && - this.certificationRevocationSignatures[i].keyId == keyId) || - (this.certificationRevocationSignatures[i].version == 4 && - this.certificationRevocationSignatures[i].issuerKeyId == keyId)) - return this.certificationRevocationSignatures[i]; - } - return null; - } - - /** - * Verifies all certification signatures. This method does not consider possible revocation signatures. - * @param {Object} publicKeyPacket the top level key material - * @return {Integer[]} An array of integers corresponding to the array of certification signatures. The meaning of each integer is the following: - * 0 = bad signature - * 1 = signature expired - * 2 = issuer key not available - * 3 = revoked - * 4 = signature valid - * 5 = signature by key owner expired - * 6 = signature by key owner revoked - */ - this.verifyCertificationSignatures = function(publicKeyPacket) { - var bytes = this.get_text_bytes(); - result = new Array(); - for (var i = 0 ; i < this.certificationSignatures.length; i++) { - // A certification signature (type 0x10 through 0x13) hashes the User - // ID being bound to the key into the hash context after the above - // data. A V3 certification hashes the contents of the User ID or - // attribute packet packet, without any header. A V4 certification - // hashes the constant 0xB4 for User ID certifications or the constant - // 0xD1 for User Attribute certifications, followed by a four-octet - // number giving the length of the User ID or User Attribute data, and - // then the User ID or User Attribute data. - - if (this.certificationSignatures[i].version == 4) { - if (this.certificationSignatures[i].signatureExpirationTime != null && - this.certificationSignatures[i].signatureExpirationTime != null && - this.certificationSignatures[i].signatureExpirationTime != 0 && - !this.certificationSignatures[i].signatureNeverExpires && - new Date(this.certificationSignatures[i].creationTime.getTime() +(this.certificationSignatures[i].signatureExpirationTime*1000)) < new Date()) { - if (this.certificationSignatures[i].issuerKeyId == publicKeyPacket.getKeyId()) - result[i] = 5; - else - result[i] = 1; - continue; - } - if (this.certificationSignatures[i].issuerKeyId == null) { - result[i] = 0; - continue; - } - var issuerPublicKey = openpgp.keyring.getPublicKeysForKeyId(this.certificationSignatures[i].issuerKeyId); - if (issuerPublicKey == null || issuerPublicKey.length == 0) { - result[i] = 2; - continue; - } - // TODO: try to verify all returned issuer public keys (key ids are not unique!) - var issuerPublicKey = issuerPublicKey[0]; - var signingKey = issuerPublicKey.obj.getSigningKey(); - if (signingKey == null) { - result[i] = 0; - continue; - } - var revocation = this.hasCertificationRevocationSignature(this.certificationSignatures[i].issuerKeyId); - if (revocation != null && revocation.creationTime > - this.certificationSignatures[i].creationTime) { - - var signaturedata = String.fromCharCode(0x99)+ publicKeyPacket.header.substring(1)+ - publicKeyPacket.data+String.fromCharCode(0xB4)+ - String.fromCharCode((bytes.length >> 24) & 0xFF)+ - String.fromCharCode((bytes.length >> 16) & 0xFF)+ - String.fromCharCode((bytes.length >> 8) & 0xFF)+ - String.fromCharCode((bytes.length) & 0xFF)+ - bytes; - if (revocation.verify(signaturedata, signingKey)) { - if (this.certificationSignatures[i].issuerKeyId == publicKeyPacket.getKeyId()) - result[i] = 6; - else - result[i] = 3; - continue; - } - } - - var signaturedata = String.fromCharCode(0x99)+ publicKeyPacket.header.substring(1)+ - publicKeyPacket.data+String.fromCharCode(0xB4)+ - String.fromCharCode((bytes.length >> 24) & 0xFF)+ - String.fromCharCode((bytes.length >> 16) & 0xFF)+ - String.fromCharCode((bytes.length >> 8) & 0xFF)+ - String.fromCharCode((bytes.length) & 0xFF)+ - bytes; - if (this.certificationSignatures[i].verify(signaturedata, signingKey)) { - result[i] = 4; - } else - result[i] = 0; - } else if (this.certificationSignatures[i].version == 3) { - if (this.certificationSignatures[i].keyId == null) { - result[i] = 0; - continue; - } - var issuerPublicKey = openpgp.keyring.getPublicKeysForKeyId(this.certificationSignatures[i].keyId); - if (issuerPublicKey == null || issuerPublicKey.length == 0) { - result[i] = 2; - continue; - } - issuerPublicKey = issuerPublicKey[0]; - var signingKey = publicKey.obj.getSigningKey(); - if (signingKey == null) { - result[i] = 0; - continue; - } - var revocation = this.hasCertificationRevocationSignature(this.certificationSignatures[i].keyId); - if (revocation != null && revocation.creationTime > - this.certificationSignatures[i].creationTime) { - var signaturedata = String.fromCharCode(0x99)+ this.publicKeyPacket.header.substring(1)+ - this.publicKeyPacket.data+bytes; - if (revocation.verify(signaturedata, signingKey)) { - if (revocation.keyId == publicKeyPacket.getKeyId()) - result[i] = 6; - else - result[i] = 3; - continue; - } - } - var signaturedata = String.fromCharCode(0x99)+ publicKeyPacket.header.substring(1)+ - publicKeyPacket.data + bytes; - if (this.certificationSignatures[i].verify(signaturedata, signingKey)) { - result[i] = 4; - } else - result[i] = 0; - } else { - result[i] = 0; - } - } - return result; - } - - /** - * verifies the signatures of the user id - * @return 0 if the userid is valid; 1 = userid expired; 2 = userid revoked - */ - this.verify = function(publicKeyPacket) { - var result = this.verifyCertificationSignatures(publicKeyPacket); - if (result.indexOf(6) != -1) - return 2; - if (result.indexOf(5) != -1) - return 1; - return 0; - } - - // TODO: implementation missing - this.addCertification = function(publicKeyPacket, privateKeyPacket) { - - } - - // TODO: implementation missing - this.revokeCertification = function(publicKeyPacket, privateKeyPacket) { - - } -} -// 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 Parent openpgp packet class. Operations focus on determining @@ -11742,7 +11146,8 @@ function openpgp_packet_public_key_encrypted_session_key() { */ this.read = function(bytes) { if (bytes.length < 10) { - util.print_error("openpgp.packet.encryptedsessionkey.js\n" + 'invalid length'); + util.print_error("openpgp.packet.encryptedsessionkey.js\n" + + 'invalid length'); return null; } @@ -11915,10 +11320,8 @@ function openpgp_packet_public_key_encrypted_session_key() { * 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; @@ -12071,35 +11474,11 @@ function openpgp_packet_public_key() { 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; - } - } - - +function openpgp_packet_public_subkey() { + openpgp_packet_public_key.call(this); + this.tag = 14; } // GPG4Browsers - An OpenPGP implementation in javascript // Copyright (C) 2011 Recurity Labs GmbH @@ -12637,6 +12016,14 @@ function openpgp_packet_secret_key() { } + + +function openpgp_packet_secret_subkey() { + openpgp_packet_secret_key.call(this); + this.tag = 7; +} + + // GPG4Browsers - An OpenPGP implementation in javascript // Copyright (C) 2011 Recurity Labs GmbH // @@ -13017,6 +12404,62 @@ function openpgp_packet_symmetrically_encrypted() { // 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 User ID Packet (Tag 13) + * A User ID packet consists of UTF-8 text that is intended to represent + * the name and email address of the key holder. By convention, it + * includes an RFC 2822 [RFC2822] mail name-addr, but there are no + * restrictions on its content. The packet length in the header + * specifies the length of the User ID. + */ + +function openpgp_packet_userid() { + /** @type {String} A string containing the user id. Usually in the form + * John Doe + */ + this.userid = ''; + this.tag = 13; + + + /** + * Parsing function for a user id packet (tag 13). + * @param {String} input payload of a tag 13 packet + * @param {Integer} position position to start reading from the input string + * @param {Integer} len length of the packet or the remaining length of input + * at position + * @return {openpgp_packet_encrypteddata} object representation + */ + this.read = function(bytes) { + this.userid = util.decode_utf8(bytes); + } + + /** + * Creates a string representation of the user id packet + * @param {String} user_id the user id as string ("John Doe d?(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.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", +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.20130426";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<>24&255);c+=String.fromCharCode(Math.round(this.date.getTime()/1E3)>>16&255);c+=String.fromCharCode(Math.round(this.date.getTime()/1E3)>>8&255);c+=String.fromCharCode(Math.round(this.date.getTime()/ -1E3)&255);return c+a};this.toString=function(){return"5.9. Literal Data Packet (Tag 11)\n length: "+this.packetLength+"\n format: "+this.format+"\n filename:"+this.filename+"\n date: "+this.date+"\n data: |"+this.data+"|\n rdata: |"+this.real_data+"|\n"}}openpgp_packet_literal.format={binary:"b",text:"t",utf8:"u"}; -function openpgp_packet_encryptedsessionkey(){this.read_pub_key_packet=function(b,a,c){this.tagType=1;this.packetLength=c;var d=a;if(10>c)return util.print_error("openpgp.packet.encryptedsessionkey.js\ninvalid length"),null;this.version=b[d++].charCodeAt();this.keyId=new openpgp_type_keyid;this.keyId.read_packet(b,d);d+=8;this.publicKeyAlgorithmUsed=b[d++].charCodeAt();switch(this.publicKeyAlgorithmUsed){case 1:case 2:this.MPIs=[];this.MPIs[0]=new openpgp_type_mpi;this.MPIs[0].read(b,d,d-a);break; -case 16:this.MPIs=[];this.MPIs[0]=new openpgp_type_mpi;this.MPIs[0].read(b,d,d-a);d+=this.MPIs[0].packetLength;this.MPIs[1]=new openpgp_type_mpi;this.MPIs[1].read(b,d,d-a);break;default:util.print_error("openpgp.packet.encryptedsessionkey.js\nunknown public key packet algorithm type "+this.publicKeyAlgorithmType)}return this};this.read_symmetric_key_packet=function(b,a,c){this.tagType=3;var d=a;this.version=b[d++];this.symmetricKeyAlgorithmUsed=b[d++];this.s2k=new openpgp_type_s2k;this.s2k.read(b, -d);if(s2k.s2kLength+d>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;cg.signatureType?this.certificationSignatures[this.certificationSignatures.length]=g:32==g.signatureType&&(this.certificationRevocationSignatures[this.certificationRevocationSignatures.length]=g);e+=g.packetLength+g.headerLength;f=d-(e-c);break;default:return this.data=a,this.position=c-b.packetLength,this.len=e-c}}this.data=a;this.position=c-b.packetLength;return this.len=e-c};this.toString=function(){for(var b="5.12. User Attribute Packet (Tag 17)\n AttributePackets: (count = "+ this.userattributes.length+")\n",a=0;ag.signatureType){this.certificationSignatures[this.certificationSignatures.length]=g;break}else if(48==g.signatureType){this.certificationRevocationSignatures[this.certificationRevocationSignatures.length]= -g;break}else if(24==g.signatureType){this.certificationSignatures[this.certificationSignatures.length]=g;break}else util.print_debug("unknown sig t: "+g.signatureType+"@"+(e-(g.packetLength+g.headerLength)));default:return this.data=a,this.position=c-b.packetLength,this.len=e-c-(g.headerLength+g.packetLength)}}this.data=a;this.position=c-b.packetLength;return this.len=e-c-(g.headerLength+g.packetLength)}if(5==b.tagType){this.parentNode=b;for(e=c;a.length!=e;)if(g=openpgp_packet.read_packet(a,e,f- -(e-c)),null==g){util.print_error("parsing ends here @:"+e+" l:"+f);break}else switch(e+=g.packetLength+g.headerLength,g.tagType){case 2:15g.signatureType?this.certificationSignatures[this.certificationSignatures.length]=g:48==g.signatureType&&(this.certificationRevocationSignatures[this.certificationRevocationSignatures.length]=g);default:return this.data=a,this.position=c-b.packetLength,this.len=e-c-(g.headerLength+g.packetLength)}}else util.print_error("unknown parent node for a userId packet "+ -b.tagType)};this.toString=function(){for(var b=" 5.11. User ID Packet (Tag 13)\n text ("+this.text.length+'): "'+this.text.replace("<","<")+'"\n',b=b+"certification signatures:\n",a=0;athis.certificationSignatures[c].creationTime){var f=String.fromCharCode(153)+b.header.substring(1)+b.data+String.fromCharCode(180)+String.fromCharCode(a.length>> -24&255)+String.fromCharCode(a.length>>16&255)+String.fromCharCode(a.length>>8&255)+String.fromCharCode(a.length&255)+a;if(e.verify(f,d)){result[c]=this.certificationSignatures[c].issuerKeyId==b.getKeyId()?6:3;continue}}f=String.fromCharCode(153)+b.header.substring(1)+b.data+String.fromCharCode(180)+String.fromCharCode(a.length>>24&255)+String.fromCharCode(a.length>>16&255)+String.fromCharCode(a.length>>8&255)+String.fromCharCode(a.length&255)+a;result[c]=this.certificationSignatures[c].verify(f,d)? -4:0}}else if(3==this.certificationSignatures[c].version)if(null==this.certificationSignatures[c].keyId)result[c]=0;else if(d=openpgp.keyring.getPublicKeysForKeyId(this.certificationSignatures[c].keyId),null==d||0==d.length)result[c]=2;else if(d=publicKey.obj.getSigningKey(),null==d)result[c]=0;else{e=this.hasCertificationRevocationSignature(this.certificationSignatures[c].keyId);if(null!=e&&e.creationTime>this.certificationSignatures[c].creationTime&&(f=String.fromCharCode(153)+this.publicKeyPacket.header.substring(1)+ -this.publicKeyPacket.data+a,e.verify(f,d))){result[c]=e.keyId==b.getKeyId()?6:3;continue}f=String.fromCharCode(153)+b.header.substring(1)+b.data+a;result[c]=this.certificationSignatures[c].verify(f,d)?4:0}else result[c]=0;return result};this.verify=function(b){b=this.verifyCertificationSignatures(b);return-1!=b.indexOf(6)?2:-1!=b.indexOf(5)?1:0};this.addCertification=function(){};this.revokeCertification=function(){}} 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:"+ @@ -420,8 +403,8 @@ var a=10;switch(this.public_key_algorithm){case openpgp.publickey.rsa_encrypt:ca 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_public_key(){this.tag=6;this.version=4;this.created=null;this.mpi=[];this.algorithm=openpgp.publickey.rsa_sign;this.read=function(b){this.version=b[0].charCodeAt();if(3!=this.version){if(4==this.version){var a=b.substr(1,4);this.created=new Date(1E3*(a[0].charCodeAt()<<24|a[1].charCodeAt()<<16|a[2].charCodeAt()<<8|a[3].charCodeAt()));this.algorithm=b[5].charCodeAt();a=0this.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}}function openpgp_packet_public_subkey(){openpgp_packet_public_key.call(this);this.tag=14} 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: ", @@ -431,7 +414,8 @@ keyType)}a=openpgp_packet.write_packet_header(tag,b.length);return{string:a+b,he 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(){}} +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_secret_subkey(){openpgp_packet_secret_key.call(this);this.tag=7} +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.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: "+ @@ -439,7 +423,7 @@ 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(aa;a++)b+=String.fromCharCode(0);this.read_packet=function(a,b){this.bytes=a.substring(b,b+8);return this};this.toString=function(){return util.hexstrdump(this.bytes)}} function openpgp_type_s2k(){this.read=function(b,a){var c=a;this.type=b[c++].charCodeAt();switch(this.type){case 0:this.hashAlgorithm=b[c++].charCodeAt();this.s2kLength=1;break;case 1:this.hashAlgorithm=b[c++].charCodeAt();this.saltValue=b.substring(c,c+8);c+=8;this.s2kLength=9;break;case 3:this.hashAlgorithm=b[c++].charCodeAt();this.saltValue=b.substring(c,c+8);c+=8;this.EXPBIAS=6;var d=b[c++].charCodeAt();this.count=16+(d&15)<<(d>>4)+this.EXPBIAS;this.s2kLength=10;break;case 101:"GNU"==b.substring(c+ diff --git a/src/packet/openpgp.packet.marker.js b/src/packet/marker.js similarity index 74% rename from src/packet/openpgp.packet.marker.js rename to src/packet/marker.js index 848f746a..a7051742 100644 --- a/src/packet/openpgp.packet.marker.js +++ b/src/packet/marker.js @@ -27,7 +27,8 @@ * Such a packet MUST be ignored when received. */ function openpgp_packet_marker() { - this.tagType = 10; + this.tag = 10; + /** * Parsing function for a literal data packet (tag 10). * @@ -39,27 +40,12 @@ function openpgp_packet_marker() { * input at position * @return {openpgp_packet_encrypteddata} Object representation */ - function read_packet(input, position, len) { - this.packetLength = 3; - if (input[position].charCodeAt() == 0x50 && // P - input[position + 1].charCodeAt() == 0x47 && // G - input[position + 2].charCodeAt() == 0x50) // P - return this; + this.read = function(bytes) { + if (bytes[0].charCodeAt() == 0x50 && // P + bytes[1].charCodeAt() == 0x47 && // G + bytes[2].charCodeAt() == 0x50) // P + return true; // marker packet does not contain "PGP" - return null; + return false; } - - /** - * Generates Debug output - * - * @return {String} String which gives some information about the - * keymaterial - */ - function toString() { - return "5.8. Marker Packet (Obsolete Literal Packet) (Tag 10)\n" - + " packet reads: \"PGP\"\n"; - } - - this.read_packet = read_packet; - this.toString = toString; } diff --git a/src/packet/openpgp.packet.encryptedsessionkey.js b/src/packet/openpgp.packet.encryptedsessionkey.js deleted file mode 100644 index 24a4961c..00000000 --- a/src/packet/openpgp.packet.encryptedsessionkey.js +++ /dev/null @@ -1,226 +0,0 @@ -// 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 Public-Key Encrypted Session Key Packets (Tag 1) - * - * RFC4880 5.1: A Public-Key Encrypted Session Key packet holds the session key - * used to encrypt a message. Zero or more Public-Key Encrypted Session Key - * packets and/or Symmetric-Key Encrypted Session Key packets may precede a - * Symmetrically Encrypted Data Packet, which holds an encrypted message. The - * message is encrypted with the session key, and the session key is itself - * encrypted and stored in the Encrypted Session Key packet(s). The - * Symmetrically Encrypted Data Packet is preceded by one Public-Key Encrypted - * Session Key packet for each OpenPGP key to which the message is encrypted. - * The recipient of the message finds a session key that is encrypted to their - * public key, decrypts the session key, and then uses the session key to - * decrypt the message. - */ -function openpgp_packet_encryptedsessionkey() { - - /** - * Parsing function for a publickey encrypted session key packet (tag 1). - * - * @param {String} input Payload of a tag 1 packet - * @param {Integer} position Position to start reading from the input string - * @param {Integer} len Length of the packet or the remaining length of - * input at position - * @return {openpgp_packet_encrypteddata} Object representation - */ - function read_pub_key_packet(input, position, len) { - this.tagType = 1; - this.packetLength = len; - var mypos = position; - if (len < 10) { - util - .print_error("openpgp.packet.encryptedsessionkey.js\n" + 'invalid length'); - return null; - } - - this.version = input[mypos++].charCodeAt(); - this.keyId = new openpgp_type_keyid(); - this.keyId.read_packet(input, mypos); - mypos += 8; - this.publicKeyAlgorithmUsed = input[mypos++].charCodeAt(); - - switch (this.publicKeyAlgorithmUsed) { - case 1: - case 2: // RSA - this.MPIs = new Array(); - this.MPIs[0] = new openpgp_type_mpi(); - this.MPIs[0].read(input, mypos, mypos - position); - break; - case 16: // Elgamal - this.MPIs = new Array(); - this.MPIs[0] = new openpgp_type_mpi(); - this.MPIs[0].read(input, mypos, mypos - position); - mypos += this.MPIs[0].packetLength; - this.MPIs[1] = new openpgp_type_mpi(); - this.MPIs[1].read(input, mypos, mypos - position); - break; - default: - util.print_error("openpgp.packet.encryptedsessionkey.js\n" - + "unknown public key packet algorithm type " - + this.publicKeyAlgorithmType); - break; - } - return this; - } - - /** - * Create a string representation of a tag 1 packet - * - * @param {String} publicKeyId - * The public key id corresponding to publicMPIs key as string - * @param {openpgp_type_mpi[]} publicMPIs - * Multiprecision integer objects describing the public key - * @param {Integer} pubalgo - * The corresponding public key algorithm // See RFC4880 9.1 - * @param {Integer} symmalgo - * The symmetric cipher algorithm used to encrypt the data - * within an encrypteddatapacket or encryptedintegrity- - * protecteddatapacket - * following this packet //See RFC4880 9.2 - * @param {String} sessionkey - * A string of randombytes representing the session key - * @return {String} The string representation - */ - function write_pub_key_packet(publicKeyId, publicMPIs, pubalgo, symmalgo, - sessionkey) { - var result = String.fromCharCode(3); - var data = String.fromCharCode(symmalgo); - data += sessionkey; - var checksum = util.calc_checksum(sessionkey); - data += String.fromCharCode((checksum >> 8) & 0xFF); - data += String.fromCharCode((checksum) & 0xFF); - result += publicKeyId; - result += String.fromCharCode(pubalgo); - var mpi = new openpgp_type_mpi(); - var mpiresult = openpgp_crypto_asymetricEncrypt(pubalgo, publicMPIs, - mpi.create(openpgp_encoding_eme_pkcs1_encode(data, - publicMPIs[0].mpiByteLength))); - for ( var i = 0; i < mpiresult.length; i++) { - result += mpiresult[i]; - } - result = openpgp_packet.write_packet_header(1, result.length) + result; - return result; - } - - /** - * Parsing function for a symmetric encrypted session key packet (tag 3). - * - * @param {String} input Payload of a tag 1 packet - * @param {Integer} position Position to start reading from the input string - * @param {Integer} len - * Length of the packet or the remaining length of - * input at position - * @return {openpgp_packet_encrypteddata} Object representation - */ - function read_symmetric_key_packet(input, position, len) { - this.tagType = 3; - var mypos = position; - // A one-octet version number. The only currently defined version is 4. - this.version = input[mypos++]; - - // A one-octet number describing the symmetric algorithm used. - this.symmetricKeyAlgorithmUsed = input[mypos++]; - // A string-to-key (S2K) specifier, length as defined above. - this.s2k = new openpgp_type_s2k(); - this.s2k.read(input, mypos); - - // Optionally, the encrypted session key itself, which is decrypted - // with the string-to-key object. - if ((s2k.s2kLength + mypos) < len) { - this.encryptedSessionKey = new Array(); - for ( var i = (mypos - position); i < len; i++) { - this.encryptedSessionKey[i] = input[mypos++]; - } - } - return this; - } - /** - * Decrypts the session key (only for public key encrypted session key - * packets (tag 1) - * - * @param {openpgp_msg_message} msg - * The message object (with member encryptedData - * @param {openpgp_msg_privatekey} key - * Private key with secMPIs unlocked - * @return {String} The unencrypted session key - */ - function decrypt(msg, key) { - if (this.tagType == 1) { - var result = openpgp_crypto_asymetricDecrypt( - this.publicKeyAlgorithmUsed, key.publicKey.MPIs, - key.secMPIs, this.MPIs).toMPI(); - var checksum = ((result.charCodeAt(result.length - 2) << 8) + result - .charCodeAt(result.length - 1)); - var decoded = openpgp_encoding_eme_pkcs1_decode(result.substring(2, result.length - 2), key.publicKey.MPIs[0].getByteLength()); - var sesskey = decoded.substring(1); - var algo = decoded.charCodeAt(0); - if (msg.encryptedData.tagType == 18) - return msg.encryptedData.decrypt(algo, sesskey); - else - return msg.encryptedData.decrypt_sym(algo, sesskey); - } else if (this.tagType == 3) { - util - .print_error("Symmetric encrypted sessionkey is not supported!"); - return null; - } - } - - /** - * Creates a string representation of this object (useful for debug - * purposes) - * - * @return {String} The string containing a openpgp description - */ - function toString() { - if (this.tagType == 1) { - var result = '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'; - for ( var i = 0; i < this.MPIs.length; i++) { - result += this.MPIs[i].toString(); - } - return result; - } else - return '5.3 Symmetric-Key Encrypted Session Key Packets (Tag 3)\n' - + ' KeyId: ' + this.keyId.toString() + '\n' - + ' length: ' + this.packetLength + '\n' - + ' version:' + this.version + '\n' + ' symKeyA:' - + this.symmetricKeyAlgorithmUsed + '\n' + ' s2k: ' - + this.s2k + '\n'; - } - - this.read_pub_key_packet = read_pub_key_packet; - this.read_symmetric_key_packet = read_symmetric_key_packet; - this.write_pub_key_packet = write_pub_key_packet; - this.toString = toString; - this.decrypt = decrypt; -}; - diff --git a/src/packet/openpgp.packet.userid.js b/src/packet/openpgp.packet.userid.js deleted file mode 100644 index de4a6dd0..00000000 --- a/src/packet/openpgp.packet.userid.js +++ /dev/null @@ -1,356 +0,0 @@ -// 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 User ID Packet (Tag 13) - * A User ID packet consists of UTF-8 text that is intended to represent - * the name and email address of the key holder. By convention, it - * includes an RFC 2822 [RFC2822] mail name-addr, but there are no - * restrictions on its content. The packet length in the header - * specifies the length of the User ID. - */ - -function openpgp_packet_userid() { - this.text = '' - this.tagType = 13; - this.certificationSignatures = new Array(); - this.certificationRevocationSignatures = new Array(); - this.revocationSignatures = new Array(); - this.parentNode = null; - - /** - * Set the packet text field to a native javascript string - * Conversion to a proper utf8 encoding takes place when the - * packet is written. - * @param {String} str Any native javascript string - */ - this.set_text = function(str) { - this.text = str; - } - - /** - * Set the packet text to value represented by the provided string - * of bytes. - * @param {String} bytes A string of bytes - */ - this.set_text_bytes = function(bytes) { - this.text = util.decode_utf8(bytes); - } - - /** - * Get the byte sequence representing the text of this packet. - * @returns {String} A sequence of bytes - */ - this.get_text_bytes = function() { - return util.encode_utf8(this.text); - } - - - /** - * Parsing function for a user id packet (tag 13). - * @param {String} input payload of a tag 13 packet - * @param {Integer} position position to start reading from the input string - * @param {Integer} len length of the packet or the remaining length of input at position - * @return {openpgp_packet_encrypteddata} object representation - */ - this.read = function(bytes) { - this.set_text_bytes(bytes); - return this; - } - - this.read_packet = function(){}; - - /** - * Creates a string representation of the user id packet - * @param {String} user_id the user id as string ("John Doe 15 - && result.signatureType < 20) { // certification - // // - // signature - this.certificationSignatures[this.certificationSignatures.length] = result; - break; - } else if (result.signatureType == 48) {// certification revocation signature - this.certificationRevocationSignatures[this.certificationRevocationSignatures.length] = result; - break; - } else if (result.signatureType == 24) { // omg. standalone signature - this.certificationSignatures[this.certificationSignatures.length] = result; - break; - } else { - util.print_debug("unknown sig t: "+result.signatureType+"@"+(pos - (result.packetLength + result.headerLength))); - } - default: - this.data = input; - this.position = position - parent_node.packetLength; - this.len = pos - position -(result.headerLength + result.packetLength); - return this.len; - } - } - } - this.data = input; - this.position = position - parent_node.packetLength; - this.len = pos - position -(result.headerLength + result.packetLength); - return this.len; - } else if (parent_node.tagType == 5) { // secret Key - this.parentNode = parent_node; - var exit = false; - var pos = position; - while (input.length != pos) { - var result = openpgp_packet.read_packet(input, pos, l - (pos - position)); - if (result == null) { - util.print_error('parsing ends here @:' + pos + " l:" + l); - break; - } else { - pos += result.packetLength + result.headerLength; - l = input.length - pos; - switch (result.tagType) { - case 2: // Signature Packet certification signature - if (result.signatureType > 15 - && result.signatureType < 20) - this.certificationSignatures[this.certificationSignatures.length] = result; - // certification revocation signature - else if (result.signatureType == 48) - this.certificationRevocationSignatures[this.certificationRevocationSignatures.length] = result; - default: - this.data = input; - this.position = position - parent_node.packetLength; - this.len = pos - position -(result.headerLength + result.packetLength); - return this.len; - } - } - } - } else { - util.print_error("unknown parent node for a userId packet "+parent_node.tagType); - } - } - - /** - * generates debug output (pretty print) - * @return {String} String which gives some information about the user id packet - */ - this.toString = function() { - var result = ' 5.11. User ID Packet (Tag 13)\n' + ' text (' - + this.text.length + '): "' + this.text.replace("<", "<") - + '"\n'; - result +="certification signatures:\n"; - for (var i = 0; i < this.certificationSignatures.length; i++) { - result += " "+this.certificationSignatures[i].toString(); - } - result +="certification revocation signatures:\n"; - for (var i = 0; i < this.certificationRevocationSignatures.length; i++) { - result += " "+this.certificationRevocationSignatures[i].toString(); - } - return result; - } - - /** - * lookup function to find certification revocation signatures - * @param {String} keyId string containing the key id of the issuer of this signature - * @return a CertificationRevocationSignature if found; otherwise null - */ - this.hasCertificationRevocationSignature = function(keyId) { - for (var i = 0; i < this.certificationRevocationSignatures.length; i++) { - if ((this.certificationRevocationSignatures[i].version == 3 && - this.certificationRevocationSignatures[i].keyId == keyId) || - (this.certificationRevocationSignatures[i].version == 4 && - this.certificationRevocationSignatures[i].issuerKeyId == keyId)) - return this.certificationRevocationSignatures[i]; - } - return null; - } - - /** - * Verifies all certification signatures. This method does not consider possible revocation signatures. - * @param {Object} publicKeyPacket the top level key material - * @return {Integer[]} An array of integers corresponding to the array of certification signatures. The meaning of each integer is the following: - * 0 = bad signature - * 1 = signature expired - * 2 = issuer key not available - * 3 = revoked - * 4 = signature valid - * 5 = signature by key owner expired - * 6 = signature by key owner revoked - */ - this.verifyCertificationSignatures = function(publicKeyPacket) { - var bytes = this.get_text_bytes(); - result = new Array(); - for (var i = 0 ; i < this.certificationSignatures.length; i++) { - // A certification signature (type 0x10 through 0x13) hashes the User - // ID being bound to the key into the hash context after the above - // data. A V3 certification hashes the contents of the User ID or - // attribute packet packet, without any header. A V4 certification - // hashes the constant 0xB4 for User ID certifications or the constant - // 0xD1 for User Attribute certifications, followed by a four-octet - // number giving the length of the User ID or User Attribute data, and - // then the User ID or User Attribute data. - - if (this.certificationSignatures[i].version == 4) { - if (this.certificationSignatures[i].signatureExpirationTime != null && - this.certificationSignatures[i].signatureExpirationTime != null && - this.certificationSignatures[i].signatureExpirationTime != 0 && - !this.certificationSignatures[i].signatureNeverExpires && - new Date(this.certificationSignatures[i].creationTime.getTime() +(this.certificationSignatures[i].signatureExpirationTime*1000)) < new Date()) { - if (this.certificationSignatures[i].issuerKeyId == publicKeyPacket.getKeyId()) - result[i] = 5; - else - result[i] = 1; - continue; - } - if (this.certificationSignatures[i].issuerKeyId == null) { - result[i] = 0; - continue; - } - var issuerPublicKey = openpgp.keyring.getPublicKeysForKeyId(this.certificationSignatures[i].issuerKeyId); - if (issuerPublicKey == null || issuerPublicKey.length == 0) { - result[i] = 2; - continue; - } - // TODO: try to verify all returned issuer public keys (key ids are not unique!) - var issuerPublicKey = issuerPublicKey[0]; - var signingKey = issuerPublicKey.obj.getSigningKey(); - if (signingKey == null) { - result[i] = 0; - continue; - } - var revocation = this.hasCertificationRevocationSignature(this.certificationSignatures[i].issuerKeyId); - if (revocation != null && revocation.creationTime > - this.certificationSignatures[i].creationTime) { - - var signaturedata = String.fromCharCode(0x99)+ publicKeyPacket.header.substring(1)+ - publicKeyPacket.data+String.fromCharCode(0xB4)+ - String.fromCharCode((bytes.length >> 24) & 0xFF)+ - String.fromCharCode((bytes.length >> 16) & 0xFF)+ - String.fromCharCode((bytes.length >> 8) & 0xFF)+ - String.fromCharCode((bytes.length) & 0xFF)+ - bytes; - if (revocation.verify(signaturedata, signingKey)) { - if (this.certificationSignatures[i].issuerKeyId == publicKeyPacket.getKeyId()) - result[i] = 6; - else - result[i] = 3; - continue; - } - } - - var signaturedata = String.fromCharCode(0x99)+ publicKeyPacket.header.substring(1)+ - publicKeyPacket.data+String.fromCharCode(0xB4)+ - String.fromCharCode((bytes.length >> 24) & 0xFF)+ - String.fromCharCode((bytes.length >> 16) & 0xFF)+ - String.fromCharCode((bytes.length >> 8) & 0xFF)+ - String.fromCharCode((bytes.length) & 0xFF)+ - bytes; - if (this.certificationSignatures[i].verify(signaturedata, signingKey)) { - result[i] = 4; - } else - result[i] = 0; - } else if (this.certificationSignatures[i].version == 3) { - if (this.certificationSignatures[i].keyId == null) { - result[i] = 0; - continue; - } - var issuerPublicKey = openpgp.keyring.getPublicKeysForKeyId(this.certificationSignatures[i].keyId); - if (issuerPublicKey == null || issuerPublicKey.length == 0) { - result[i] = 2; - continue; - } - issuerPublicKey = issuerPublicKey[0]; - var signingKey = publicKey.obj.getSigningKey(); - if (signingKey == null) { - result[i] = 0; - continue; - } - var revocation = this.hasCertificationRevocationSignature(this.certificationSignatures[i].keyId); - if (revocation != null && revocation.creationTime > - this.certificationSignatures[i].creationTime) { - var signaturedata = String.fromCharCode(0x99)+ this.publicKeyPacket.header.substring(1)+ - this.publicKeyPacket.data+bytes; - if (revocation.verify(signaturedata, signingKey)) { - if (revocation.keyId == publicKeyPacket.getKeyId()) - result[i] = 6; - else - result[i] = 3; - continue; - } - } - var signaturedata = String.fromCharCode(0x99)+ publicKeyPacket.header.substring(1)+ - publicKeyPacket.data + bytes; - if (this.certificationSignatures[i].verify(signaturedata, signingKey)) { - result[i] = 4; - } else - result[i] = 0; - } else { - result[i] = 0; - } - } - return result; - } - - /** - * verifies the signatures of the user id - * @return 0 if the userid is valid; 1 = userid expired; 2 = userid revoked - */ - this.verify = function(publicKeyPacket) { - var result = this.verifyCertificationSignatures(publicKeyPacket); - if (result.indexOf(6) != -1) - return 2; - if (result.indexOf(5) != -1) - return 1; - return 0; - } - - // TODO: implementation missing - this.addCertification = function(publicKeyPacket, privateKeyPacket) { - - } - - // TODO: implementation missing - this.revokeCertification = function(publicKeyPacket, privateKeyPacket) { - - } -} diff --git a/src/packet/public_key.js b/src/packet/public_key.js index f4cdc46e..eedf0f9b 100644 --- a/src/packet/public_key.js +++ b/src/packet/public_key.js @@ -25,10 +25,8 @@ * 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; @@ -181,33 +179,9 @@ function openpgp_packet_public_key() { 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; - } - } - - +} + +function openpgp_packet_public_subkey() { + openpgp_packet_public_key.call(this); + this.tag = 14; } diff --git a/src/packet/public_key_encrypted_session_key.js b/src/packet/public_key_encrypted_session_key.js index b3e0ba70..20f14bed 100644 --- a/src/packet/public_key_encrypted_session_key.js +++ b/src/packet/public_key_encrypted_session_key.js @@ -55,7 +55,8 @@ function openpgp_packet_public_key_encrypted_session_key() { */ this.read = function(bytes) { if (bytes.length < 10) { - util.print_error("openpgp.packet.encryptedsessionkey.js\n" + 'invalid length'); + util.print_error("openpgp.packet.encryptedsessionkey.js\n" + + 'invalid length'); return null; } diff --git a/src/packet/secret_key.js b/src/packet/secret_key.js index 94ec44a4..c01697bf 100644 --- a/src/packet/secret_key.js +++ b/src/packet/secret_key.js @@ -534,3 +534,11 @@ function openpgp_packet_secret_key() { } + + +function openpgp_packet_secret_subkey() { + openpgp_packet_secret_key.call(this); + this.tag = 7; +} + + diff --git a/src/packet/userid.js b/src/packet/userid.js new file mode 100644 index 00000000..63ca2bc5 --- /dev/null +++ b/src/packet/userid.js @@ -0,0 +1,56 @@ +// 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 User ID Packet (Tag 13) + * A User ID packet consists of UTF-8 text that is intended to represent + * the name and email address of the key holder. By convention, it + * includes an RFC 2822 [RFC2822] mail name-addr, but there are no + * restrictions on its content. The packet length in the header + * specifies the length of the User ID. + */ + +function openpgp_packet_userid() { + /** @type {String} A string containing the user id. Usually in the form + * John Doe + */ + this.userid = ''; + this.tag = 13; + + + /** + * Parsing function for a user id packet (tag 13). + * @param {String} input payload of a tag 13 packet + * @param {Integer} position position to start reading from the input string + * @param {Integer} len length of the packet or the remaining length of input + * at position + * @return {openpgp_packet_encrypteddata} object representation + */ + this.read = function(bytes) { + this.userid = util.decode_utf8(bytes); + } + + /** + * Creates a string representation of the user id packet + * @param {String} user_id the user id as string ("John Doe