From 38330ea4a678de29d209059ee91c60bfc64ffe6d Mon Sep 17 00:00:00 2001 From: Michal Kolodziej Date: Tue, 23 Apr 2013 10:03:28 +0200 Subject: [PATCH] Sym encrypted session key is working, decryption of messages from gpg works too! Fixed some minor bugs and got rid of a few globals. --- resources/openpgp.js | 215 +++++++++++++----- resources/openpgp.min.js | 45 ++-- src/ciphers/openpgp.crypto.js | 16 +- src/packet/compressed.js | 14 +- src/packet/packet.js | 5 +- src/packet/packetlist.js | 2 +- .../sym_encrypted_integrity_protected.js | 52 ++--- src/packet/sym_encrypted_session_key.js | 109 +++++++++ src/packet/symmetrically_encrypted.js | 1 - src/type/openpgp.type.s2k.js | 20 +- test/general/packet.js | 29 +++ 11 files changed, 378 insertions(+), 130 deletions(-) create mode 100644 src/packet/sym_encrypted_session_key.js diff --git a/resources/openpgp.js b/resources/openpgp.js index 2b93c92d..fc62bb08 100644 --- a/resources/openpgp.js +++ b/resources/openpgp.js @@ -3787,18 +3787,26 @@ function openpgp_crypto_MDCSystemBytes(algo, key, data) { * @return {String} Random bytes as a string to be used as a key */ function openpgp_crypto_generateSessionKey(algo) { + return openpgp_crypto_getRandomBytes(openpgp_crypto_getKeyLength(algo)); +} + +/** + * Get the key length by symmetric algorithm id. + * @param {Integer} algo Algorithm to use (see RFC4880 9.2) + * @return {String} Random bytes as a string to be used as a key + */ +function openpgp_crypto_getKeyLength(algo) { switch (algo) { case 2: // TripleDES (DES-EDE, [SCHNEIER] [HAC] - 168 bit key derived from 192) case 8: // AES with 192-bit key - return openpgp_crypto_getRandomBytes(24); + return 24; case 3: // CAST5 (128 bit key, as per [RFC2144]) case 4: // Blowfish (128 bit key, 16 rounds) [BLOWFISH] case 7: // AES with 128-bit key [AES] - util.print_debug("length = 16:\n"+util.hexstrdump(openpgp_crypto_getRandomBytes(16))); - return openpgp_crypto_getRandomBytes(16); + return 16; case 9: // AES with 256-bit key case 10:// Twofish with 256-bit key [TWOFISH] - return openpgp_crypto_getRandomBytes(32); + return 32; } return null; } @@ -7383,7 +7391,7 @@ function openpgp_config() { keyserver: "keyserver.linux.it" // "pgp.mit.edu:11371" }; - this.versionstring ="OpenPGP.js v.1.20130422"; + this.versionstring ="OpenPGP.js v.1.20130423"; this.commentstring ="http://openpgpjs.org"; /** * Reads the config out of the HTML5 local storage @@ -9303,7 +9311,7 @@ function openpgp_msg_publickey() { */ function openpgp_packet_compressed() { this.tag = 8; - this.packets = new openpgp_packetlist(); + this.data = new openpgp_packetlist(); this.algorithm = openpgp.compression.uncompressed; this.compressed = null; @@ -9312,15 +9320,15 @@ function openpgp_packet_compressed() { * Parsing function for the packet. * @param {String} input Payload of a tag 8 packet * @param {Integer} position Position to start reading from the input string - * @param {Integer} len Length of the packet or the remaining length of + * @parAM {iNTEGER} LEN lENGTH OF the packet or the remaining length of * input at position * @return {openpgp_packet_compressed} Object representation */ this.read = function(bytes) { // One octet that gives the algorithm used to compress the packet. - this.algorithm = input.charCodeAt(0); + this.algorithm = bytes.charCodeAt(0); // Compressed data, which makes up the remainder of the packet. - this.compressed = input.substr(1); + this.compressed = bytes.substr(1); this.decompress(); } @@ -9341,7 +9349,7 @@ function openpgp_packet_compressed() { * @return {String} The decompressed data */ this.decompress = function() { - var bytes; + var decompressed; switch (this.algorithm) { case openpgp.compression.uncompressed: @@ -9394,7 +9402,7 @@ function openpgp_packet_compressed() { util.print_debug("decompressed:"+util.hexstrdump(decompressed)); - this.packets.read(decompressed); + this.data.read(decompressed); } /** @@ -9407,7 +9415,7 @@ function openpgp_packet_compressed() { switch (this.type) { case openpgp.compression.uncompressed: // - Uncompressed - this.compressed = this.packets.write(); + this.compressed = this.data.write(); break; case openpgp.compression.zip: // - ZIP [RFC1951] @@ -12092,7 +12100,7 @@ function _openpgp_packet() { * @return {String} String with openpgp length representation */ function encode_length(length) { - result = ""; + var result = ""; if (length < 192) { result += String.fromCharCode(length); } else if (length > 191 && length < 8384) { @@ -12177,6 +12185,7 @@ function _openpgp_packet() { var mypos = position; var tag = -1; var format = -1; + var packet_length; format = 0; // 0 = old format; 1 = new format if ((input[mypos].charCodeAt() & 0x40) != 0) { @@ -12354,7 +12363,7 @@ function _openpgp_packet() { reserved: 0, public_key_encrypted_session_key: 1, signature: 2, - symmetric_key_encrypted_session_key: 3, + sym_encrypted_session_key: 3, one_pass_signature: 4, secret_key: 5, public_key: 6, @@ -12396,7 +12405,7 @@ function openpgp_packetlist() { while(i < bytes.length) { var parsed = openpgp_packet.read_packet(bytes, i, bytes.length - i); - i += parsed.offset; + i = parsed.offset; this.push(parsed.packet); } @@ -12456,22 +12465,19 @@ function openpgp_packetlist() { function openpgp_packet_sym_encrypted_integrity_protected() { this.tag = 18; this.version = 1; + /** The encrypted payload. */ this.encrypted = null; // string - this.hash = null; // string + /** @type {Boolean} + * If after decrypting the packet this is set to true, + * a modification has been detected and thus the contents + * should be discarded. + */ + this.modification = false; this.data = new openpgp_packetlist(); + /** @type {openpgp.symmetric} */ this.algorithm = openpgp.symmetric.plaintext; - /** - * Parsing function for the packet. - * - * @param {String} input Payload of a tag 18 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_encryptedintegrityprotecteddata} object - * representation - */ + this.read = function(bytes) { // - A one-octet version number. The only currently defined value is // 1. @@ -12491,18 +12497,7 @@ function openpgp_packet_sym_encrypted_integrity_protected() { this.encrypted = bytes.substr(1); } - /** - * Creates a string representation of a Sym. Encrypted Integrity Protected - * Data Packet (tag 18) (see RFC4880 5.13) - * - * @param {Integer} symmetric_algorithm - * The selected symmetric encryption algorithm to be used - * @param {String} key The key of cipher blocksize length to be used - * @param {String} data - * Plaintext data to be encrypted within the packet - * @return {String} A string representation of the packet - */ - this.write = function(symmetric_algorithm, key, data) { + this.write = function() { return String.fromCharCode(this.version) + this.encrypted; } @@ -12515,6 +12510,9 @@ function openpgp_packet_sym_encrypted_integrity_protected() { + prefixrandom.charAt(prefixrandom.length - 1) var tohash = bytes; + + + // Modification detection code packet. tohash += String.fromCharCode(0xD3); tohash += String.fromCharCode(0x14); @@ -12554,12 +12552,12 @@ function openpgp_packet_sym_encrypted_integrity_protected() { util.print_debug_hexstr_dump("calc hash = ", this.hash); - this.data.read(decrypted); - // We pop the mandatory modification detection code packet. - var mdc = this.data.packets.pop(); + this.data.read(decrypted.substr(0, decrypted.length - 22)); - if(this.hash != mdc.hash) { + var mdc = decrypted.substr(decrypted.length - 20, 20); + + if(this.hash != mdc) { this.data = null; util.print_error("Decryption stopped: discovered a " + "modification of encrypted data."); @@ -12582,13 +12580,115 @@ function openpgp_packet_sym_encrypted_integrity_protected() { } }; +// 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 -function openpgp_packet_modification_detection_code() { - this.hash = null; +/** + * @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_sym_encrypted_session_key() { + this.tag = 3; + this.private_algorithm = openpgp.symmetric.plaintext; + this.algorithm = openpgp.symmetric.plaintext; + this.encrypted = null; + this.s2k = new openpgp_type_s2k(); + + /** + * 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 + */ this.read = function(bytes) { - this.hash = bytes; + // A one-octet version number. The only currently defined version is 4. + this.version = bytes[0].charCodeAt(); + + // A one-octet number describing the symmetric algorithm used. + this.private_algorithm = bytes[1].charCodeAt(); + + // A string-to-key (S2K) specifier, length as defined above. + this.s2k.read(bytes, 2); + + // Optionally, the encrypted session key itself, which is decrypted + // with the string-to-key object. + var done = this.s2k.length + 2; + if(done < bytes.length) { + this.encrypted = bytes.substr(done); + } } -} + /** + * 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 + */ + this.decrypt = function(passphrase) { + var length = openpgp_crypto_getKeyLength(this.private_algorithm); + var key = this.s2k.produce_key(passphrase, length); + + if(this.encrypted == null) { + this.key = key; + this.algorithm = this.private_algorithm; + } else { + var decrypted = openpgp_crypto_symmetricDecrypt( + this.private_algorithm, key, this.encrypted, true); + + this.algorithm = decrypted[0].keyCodeAt(); + this.key = decrypted.substr(1); + } + } + + /** + * Creates a string representation of this object (useful for debug + * purposes) + * + * @return {String} The string containing a openpgp description + */ + this.toString = function() { + 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'; + } +}; + // GPG4Browsers - An OpenPGP implementation in javascript // Copyright (C) 2011 Recurity Labs GmbH // @@ -12621,7 +12721,6 @@ function openpgp_packet_symmetrically_encrypted() { this.tag = 9; this.encrypted = null; this.data = new openpgp_packetlist(); - this.algorithm = openpgp.symmetric.plaintext; @@ -12939,6 +13038,7 @@ function openpgp_type_s2k() { util.print_error("unknown s2k type! "+this.type); break; } + this.packetLength = mypos - position; return this; } @@ -12966,10 +13066,12 @@ function openpgp_type_s2k() { */ function produce_key(passphrase, numBytes) { passphrase = util.encode_utf8(passphrase); + var result; + if (this.type == 0) { - return openpgp_crypto_hashData(this.hashAlgorithm,passphrase); + result = openpgp_crypto_hashData(this.hashAlgorithm,passphrase); } else if (this.type == 1) { - return openpgp_crypto_hashData(this.hashAlgorithm,this.saltValue+passphrase); + result = openpgp_crypto_hashData(this.hashAlgorithm,this.saltValue+passphrase); } else if (this.type == 3) { var isp = []; isp[0] = this.saltValue+passphrase; @@ -12978,12 +13080,19 @@ function openpgp_type_s2k() { isp = isp.join(''); if (isp.length > this.count) isp = isp.substr(0, this.count); - if(numBytes && (numBytes == 24 || numBytes == 32)){ //This if accounts for RFC 4880 3.7.1.1 -- If hash size is greater than block size, use leftmost bits. If blocksize larger than hash size, we need to rehash isp and prepend with 0. + if(numBytes && (numBytes == 24 || numBytes == 32)){ + //This if accounts for RFC 4880 3.7.1.1 -- If hash size is + //greater than block size, use leftmost bits. If blocksize + //larger than hash size, we need to rehash isp and prepend with 0. + var key = openpgp_crypto_hashData(this.hashAlgorithm,isp); - return key + openpgp_crypto_hashData(this.hashAlgorithm,String.fromCharCode(0)+isp); + result = key + openpgp_crypto_hashData(this.hashAlgorithm, + String.fromCharCode(0)+isp); } - return openpgp_crypto_hashData(this.hashAlgorithm,isp); + else result = openpgp_crypto_hashData(this.hashAlgorithm,isp); } else return null; + + return result.substr(0, numBytes); } this.read = read; diff --git a/resources/openpgp.min.js b/resources/openpgp.min.js index ecdcdd97..ea52ec38 100644 --- a/resources/openpgp.min.js +++ b/resources/openpgp.min.js @@ -111,7 +111,7 @@ function openpgp_crypto_asymetricEncrypt(b,a,c){switch(b){case 1:case 2:case 3:v function openpgp_crypto_asymetricDecrypt(b,a,c,d){switch(b){case 1:case 2:case 3:var b=new RSA,e=c[0].toBigInteger(),a=c[1].toBigInteger(),f=c[2].toBigInteger(),c=c[3].toBigInteger(),d=d[0].toBigInteger();return b.decrypt(d,e,a,f,c);case 16:return b=new Elgamal,c=c[0].toBigInteger(),e=d[0].toBigInteger(),d=d[1].toBigInteger(),a=a[0].toBigInteger(),b.decrypt(e,d,a,c);default:return null}} function openpgp_crypto_getPrefixRandom(b){switch(b){case 2:case 3:case 4:return openpgp_crypto_getRandomBytes(8);case 7:case 8:case 9:case 10:return openpgp_crypto_getRandomBytes(16);default:return null}} function openpgp_crypto_MDCSystemBytes(b,a,c){util.print_debug_hexstr_dump("openpgp_crypto_symmetricDecrypt:\nencrypteddata:",c);switch(b){case 0:return c;case 2:return openpgp_cfb_mdc(desede,8,a,c,openpgp_cfb);case 3:return openpgp_cfb_mdc(cast5_encrypt,8,a,c);case 4:return openpgp_cfb_mdc(BFencrypt,8,a,c);case 7:case 8:case 9:return openpgp_cfb_mdc(AESencrypt,16,keyExpansion(a),c);case 10:return openpgp_cfb_mdc(TFencrypt,16,a,c);case 1:util.print_error(""+(1==b?"IDEA Algorithm not implemented": -"Twofish Algorithm not implemented"))}return null}function openpgp_crypto_generateSessionKey(b){switch(b){case 2:case 8:return openpgp_crypto_getRandomBytes(24);case 3:case 4:case 7:return util.print_debug("length = 16:\n"+util.hexstrdump(openpgp_crypto_getRandomBytes(16))),openpgp_crypto_getRandomBytes(16);case 9:case 10:return openpgp_crypto_getRandomBytes(32)}return null} +"Twofish Algorithm not implemented"))}return null}function openpgp_crypto_generateSessionKey(b){return openpgp_crypto_getRandomBytes(openpgp_crypto_getKeyLength(b))}function openpgp_crypto_getKeyLength(b){switch(b){case 2:case 8:return 24;case 3:case 4:case 7:return 16;case 9:case 10:return 32}return null} function openpgp_crypto_verifySignature(b,a,c,d,e){var f=openpgp_crypto_hashData(a,e);switch(b){case 1:case 2:case 3:e=new RSA;b=d[0].toBigInteger();d=d[1].toBigInteger();c=c[0].toBigInteger();d=e.verify(c,d,b);a=openpgp_encoding_emsa_pkcs1_decode(a,d.toMPI().substring(2));return-1==a?(util.print_error("PKCS1 padding in message or key incorrect. Aborting..."),!1):a==f;case 16:return util.print_error("signing with Elgamal is not defined in the OpenPGP standard."),null;case 17:var b=new DSA,f=c[0].toBigInteger(), c=c[1].toBigInteger(),g=d[0].toBigInteger(),h=d[1].toBigInteger(),j=d[2].toBigInteger(),d=d[3].toBigInteger(),d=b.verify(a,f,c,e,g,h,j,d);return 0==d.compareTo(f);default:return null}} function openpgp_crypto_signData(b,a,c,d,e){switch(a){case 1:case 2:case 3:var a=new RSA,d=d[0].toBigInteger(),f=c[0].toBigInteger(),b=openpgp_encoding_emsa_pkcs1_encode(b,e,c[0].mpiByteLength);util.print_debug("signing using RSA");return a.sign(b,d,f).toMPI();case 17:a=new DSA;util.print_debug("DSA Sign: q size in Bytes:"+c[1].getByteLength());var f=c[0].toBigInteger(),g=c[1].toBigInteger(),h=c[2].toBigInteger();c[3].toBigInteger();c=d[0].toBigInteger();b=a.sign(b,e,h,f,g,c);util.print_debug("signing using DSA\n result:"+ @@ -285,7 +285,7 @@ JXG.Util.asciiCharCodeAt=function(b,a){var c=b.charCodeAt(a);if(255d?(a.push(String.fromCharCode(d)),c++):191d?(e=b.charCodeAt(c+1),a.push(String.fromCharCode((d&31)<<6|e&63)),c+=2):(e=b.charCodeAt(c+1),f=b.charCodeAt(c+2),a.push(String.fromCharCode((d&15)<<12|(e&63)<<6|f&63)),c+=3);return a.join("")}; JXG.Util.genUUID=function(){for(var b="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".split(""),a=Array(36),c=0,d,e=0;36>e;e++)8==e||13==e||18==e||23==e?a[e]="-":14==e?a[e]="4":(2>=c&&(c=33554432+16777216*Math.random()|0),d=c&15,c>>=4,a[e]=b[19==e?d&3|8:d]);return a.join("")}; -function openpgp_config(){this.config=null;this.default_config={prefer_hash_algorithm:8,encryption_cipher:9,compression:1,show_version:!0,show_comment:!0,integrity_protect:!0,composition_behavior:0,keyserver:"keyserver.linux.it"};this.versionstring="OpenPGP.js v.1.20130422";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.20130423";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"}; @@ -436,26 +436,27 @@ this.certificationSignatures[c].issuerKeyId)result[c]=0;else{var d=openpgp.keyri 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){result="";192>a?result+=String.fromCharCode(a):191a?(result+=String.fromCharCode((a-192>>8)+192),result+=String.fromCharCode(a-192&255)):(result+=String.fromCharCode(255),result+=String.fromCharCode(a>>24&255),result+=String.fromCharCode(a>>16&255),result+=String.fromCharCode(a>>8&255),result+=String.fromCharCode(a&255));return result}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())packet_length=a[b++].charCodeAt(),util.print_debug("1 byte length:"+packet_length);else if(192<=a[b].charCodeAt()&&224>a[b].charCodeAt())packet_length=(a[b++].charCodeAt()-192<<8)+a[b++].charCodeAt()+192,util.print_debug("2 byte length:"+packet_length);else if(223a[b].charCodeAt()){packet_length= -1<<(a[b++].charCodeAt()&31);util.print_debug("4 byte length:"+packet_length);d=b+packet_length;for(h=a.substring(b,b+packet_length);;)if(192>a[d].charCodeAt()){g=a[d++].charCodeAt();packet_length+=g;h+=a.substring(d,d+g);d+=g;break}else if(192<=a[d].charCodeAt()&&224>a[d].charCodeAt()){g=(a[d++].charCodeAt()-192<<8)+a[d++].charCodeAt()+192;packet_length+=g;h+=a.substring(d,d+g);d+=g;break}else if(223a[d].charCodeAt())g=1<<(a[d++].charCodeAt()&31),packet_length+=g,h+=a.substring(d, -d+g),d+=g;else{d++;g=a[d++].charCodeAt()<<24|a[d++].charCodeAt()<<16|a[d++].charCodeAt()<<8|a[d++].charCodeAt();h+=a.substring(d,d+g);packet_length+=g;d+=g;break}j=d}else b++,packet_length=a[b++].charCodeAt()<<24|a[b++].charCodeAt()<<16|a[b++].charCodeAt()<<8|a[b++].charCodeAt();else switch(g){case 0:packet_length=a[b++].charCodeAt();break;case 1:packet_length=a[b++].charCodeAt()<<8|a[b++].charCodeAt();break;case 2:packet_length=a[b++].charCodeAt()<<24|a[b++].charCodeAt()<<16|a[b++].charCodeAt()<< -8|a[b++].charCodeAt();break;default:packet_length=d}-1==j&&(j=packet_length);null==h&&(h=a.substring(b,b+j));var a={},k;for(k in this.type)a[this.type[k]]=k;k="openpgp_packet_"+a[e];a=window[k];if(void 0==a)throw k;k=new a;k.read(h);return{packet:k,offset:b+packet_length}};this.type={reserved:0,public_key_encrypted_session_key:1,signature:2,symmetric_key_encrypted_session_key:3,one_pass_signature:4,secret_key:5,public_key:6,secret_subkey:7,compressed:8,symmetrically_encrypted:9,marker:10,literal:11, -trust:12,userid:13,public_subkey:14,user_attribute:17,sym_encrypted_integrity_protected:18,modification_detection_code:19}}var openpgp_packet=new _openpgp_packet; -function openpgp_packetlist(){this.packets=[];this.read=function(b){this.packets=[];for(var a=0;aa?b+=String.fromCharCode(a):191a?(b+=String.fromCharCode((a-192>>8)+192),b+=String.fromCharCode(a-192&255)):(b+=String.fromCharCode(255),b+=String.fromCharCode(a>>24&255),b+=String.fromCharCode(a>>16&255),b+=String.fromCharCode(a>>8&255),b+=String.fromCharCode(a&255));return b}this.encode_length=b;this.write_old_packet_header=function(a,b){var d="";256>b?(d+=String.fromCharCode(128|a<<2),d+=String.fromCharCode(b)):(65536>b?(d+=String.fromCharCode(a<< +2|129),d+=String.fromCharCode(b>>8)):(d+=String.fromCharCode(a<<2|130),d+=String.fromCharCode(b>>24&255),d+=String.fromCharCode(b>>16&255),d+=String.fromCharCode(b>>8&255)),d+=String.fromCharCode(b&255));return d};this.write_packet_header=function(a,c){var d;d=""+String.fromCharCode(192|a);return d+=b(c)};this.read_packet=function(a,b,d){if(null==a||a.length<=b||2>a.substring(b).length||0==(a[b].charCodeAt()&128))return util.print_error("Error during parsing. This message / key is probably not containing a valid OpenPGP format."), +null;var e=-1,f=-1,f=0;0!=(a[b].charCodeAt()&64)&&(f=1);var g;f?e=a[b].charCodeAt()&63:(e=(a[b].charCodeAt()&63)>>2,g=a[b].charCodeAt()&3);b++;var h=null,j=-1;if(f)if(192>a[b].charCodeAt())d=a[b++].charCodeAt(),util.print_debug("1 byte length:"+d);else if(192<=a[b].charCodeAt()&&224>a[b].charCodeAt())d=(a[b++].charCodeAt()-192<<8)+a[b++].charCodeAt()+192,util.print_debug("2 byte length:"+d);else if(223a[b].charCodeAt()){d=1<<(a[b++].charCodeAt()&31);util.print_debug("4 byte length:"+ +d);g=b+d;for(h=a.substring(b,b+d);;)if(192>a[g].charCodeAt()){j=a[g++].charCodeAt();d+=j;h+=a.substring(g,g+j);g+=j;break}else if(192<=a[g].charCodeAt()&&224>a[g].charCodeAt()){j=(a[g++].charCodeAt()-192<<8)+a[g++].charCodeAt()+192;d+=j;h+=a.substring(g,g+j);g+=j;break}else if(223a[g].charCodeAt())j=1<<(a[g++].charCodeAt()&31),d+=j,h+=a.substring(g,g+j),g+=j;else{g++;j=a[g++].charCodeAt()<<24|a[g++].charCodeAt()<<16|a[g++].charCodeAt()<<8|a[g++].charCodeAt();h+=a.substring(g, +g+j);d+=j;g+=j;break}j=g}else b++,d=a[b++].charCodeAt()<<24|a[b++].charCodeAt()<<16|a[b++].charCodeAt()<<8|a[b++].charCodeAt();else switch(g){case 0:d=a[b++].charCodeAt();break;case 1:d=a[b++].charCodeAt()<<8|a[b++].charCodeAt();break;case 2:d=a[b++].charCodeAt()<<24|a[b++].charCodeAt()<<16|a[b++].charCodeAt()<<8|a[b++].charCodeAt();break}-1==j&&(j=d);null==h&&(h=a.substring(b,b+j));var a={},k;for(k in this.type)a[this.type[k]]=k;k="openpgp_packet_"+a[e];a=window[k];if(void 0==a)throw k;k=new a;k.read(h); +return{packet:k,offset:b+d}};this.type={reserved:0,public_key_encrypted_session_key:1,signature:2,sym_encrypted_session_key:3,one_pass_signature:4,secret_key:5,public_key:6,secret_subkey:7,compressed:8,symmetrically_encrypted:9,marker:10,literal:11,trust:12,userid:13,public_subkey:14,user_attribute:17,sym_encrypted_integrity_protected:18,modification_detection_code:19}}var openpgp_packet=new _openpgp_packet; +function openpgp_packetlist(){this.packets=[];this.read=function(b){this.packets=[];for(var a=0;ae;e++)if(0==d>>e){c=e;break a}this.mpiBitLength=a+c;this.mpiByteLength=b.length;return this};this.toBin=function(){var b=String.fromCharCode(this.mpiBitLength>>8&255),b=b+String.fromCharCode(this.mpiBitLength&255);return b+=this.MPI};this.getByteLength=function(){return this.mpiByteLength}} -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);this.s2kLength=9;break;case 3:this.hashAlgorithm=b[c++].charCodeAt();this.saltValue=b.substring(c,c+8);c+=8;this.EXPBIAS=6;c=b[c++].charCodeAt();this.count=16+(c&15)<<(c>>4)+this.EXPBIAS;this.s2kLength=10;break;case 101:"GNU"==b.substring(c+1,c+ -4)?(this.hashAlgorithm=b[c++].charCodeAt(),c+=3,c=1E3+b[c++].charCodeAt(),1001==c?(this.type=c,this.s2kLength=5):util.print_error("unknown s2k gnu protection mode! "+this.type)):util.print_error("unknown s2k type! "+this.type);break;default:util.print_error("unknown s2k type! "+this.type)}return this};this.write=function(b,a,c,d,e){this.type=b;if(3==this.type)this.saltValue=d,this.hashAlgorithm=a,this.count=16+(e&15)<<(e>>4)+6,this.s2kLength=10;return this.produce_key(c)};this.produce_key=function(b, -a){b=util.encode_utf8(b);if(0==this.type)return openpgp_crypto_hashData(this.hashAlgorithm,b);if(1==this.type)return openpgp_crypto_hashData(this.hashAlgorithm,this.saltValue+b);if(3==this.type){var c=[];for(c[0]=this.saltValue+b;c.length*(this.saltValue+b).lengththis.count&&(c=c.substr(0,this.count));return a&&(24==a||32==a)?openpgp_crypto_hashData(this.hashAlgorithm,c)+openpgp_crypto_hashData(this.hashAlgorithm,String.fromCharCode(0)+c): -openpgp_crypto_hashData(this.hashAlgorithm,c)}return null}} +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+ +1,c+4)?(this.hashAlgorithm=b[c++].charCodeAt(),c+=3,d=1E3+b[c++].charCodeAt(),1001==d?(this.type=d,this.s2kLength=5):util.print_error("unknown s2k gnu protection mode! "+this.type)):util.print_error("unknown s2k type! "+this.type);break;default:util.print_error("unknown s2k type! "+this.type)}this.packetLength=c-a;return this};this.write=function(b,a,c,d,e){this.type=b;if(3==this.type)this.saltValue=d,this.hashAlgorithm=a,this.count=16+(e&15)<<(e>>4)+6,this.s2kLength=10;return this.produce_key(c)}; +this.produce_key=function(b,a){var b=util.encode_utf8(b),c;if(0==this.type)c=openpgp_crypto_hashData(this.hashAlgorithm,b);else if(1==this.type)c=openpgp_crypto_hashData(this.hashAlgorithm,this.saltValue+b);else if(3==this.type){c=[];for(c[0]=this.saltValue+b;c.length*(this.saltValue+b).lengththis.count&&(c=c.substr(0,this.count));c=a&&(24==a||32==a)?openpgp_crypto_hashData(this.hashAlgorithm,c)+openpgp_crypto_hashData(this.hashAlgorithm, +String.fromCharCode(0)+c):openpgp_crypto_hashData(this.hashAlgorithm,c)}else return null;return c.substr(0,a)}} var Util=function(){this.emailRegEx=/[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/;this.debug=!1;this.hexdump=function(a){for(var b=[],e=a.length,f=0,g,h=0;fg.length;)g="0"+g;b.push(" "+g);h++;0==h%32&&b.push("\n ")}return b.join("")};this.hexstrdump=function(a){if(null==a)return"";for(var b=[],e=a.length,f=0,g;f g.length;)g="0"+g;b.push(""+g)}return b.join("")};this.hex2bin=function(a){for(var b="",e=0;eg.length;)g="0"+g;b.push(""+g)}return b.join("")};this.encode_utf8=function(a){return unescape(encodeURIComponent(a))};this.decode_utf8=function(a){return decodeURIComponent(escape(a))};var b=function(a,b){for(var e=0;e 191 && length < 8384) { @@ -114,6 +114,7 @@ function _openpgp_packet() { var mypos = position; var tag = -1; var format = -1; + var packet_length; format = 0; // 0 = old format; 1 = new format if ((input[mypos].charCodeAt() & 0x40) != 0) { @@ -291,7 +292,7 @@ function _openpgp_packet() { reserved: 0, public_key_encrypted_session_key: 1, signature: 2, - symmetric_key_encrypted_session_key: 3, + sym_encrypted_session_key: 3, one_pass_signature: 4, secret_key: 5, public_key: 6, diff --git a/src/packet/packetlist.js b/src/packet/packetlist.js index d00c1190..cd24a9cc 100644 --- a/src/packet/packetlist.js +++ b/src/packet/packetlist.js @@ -21,7 +21,7 @@ function openpgp_packetlist() { while(i < bytes.length) { var parsed = openpgp_packet.read_packet(bytes, i, bytes.length - i); - i += parsed.offset; + i = parsed.offset; this.push(parsed.packet); } diff --git a/src/packet/sym_encrypted_integrity_protected.js b/src/packet/sym_encrypted_integrity_protected.js index 3e564428..c4a9098a 100644 --- a/src/packet/sym_encrypted_integrity_protected.js +++ b/src/packet/sym_encrypted_integrity_protected.js @@ -30,22 +30,19 @@ function openpgp_packet_sym_encrypted_integrity_protected() { this.tag = 18; this.version = 1; + /** The encrypted payload. */ this.encrypted = null; // string - this.hash = null; // string + /** @type {Boolean} + * If after decrypting the packet this is set to true, + * a modification has been detected and thus the contents + * should be discarded. + */ + this.modification = false; this.data = new openpgp_packetlist(); + /** @type {openpgp.symmetric} */ this.algorithm = openpgp.symmetric.plaintext; - /** - * Parsing function for the packet. - * - * @param {String} input Payload of a tag 18 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_encryptedintegrityprotecteddata} object - * representation - */ + this.read = function(bytes) { // - A one-octet version number. The only currently defined value is // 1. @@ -65,18 +62,7 @@ function openpgp_packet_sym_encrypted_integrity_protected() { this.encrypted = bytes.substr(1); } - /** - * Creates a string representation of a Sym. Encrypted Integrity Protected - * Data Packet (tag 18) (see RFC4880 5.13) - * - * @param {Integer} symmetric_algorithm - * The selected symmetric encryption algorithm to be used - * @param {String} key The key of cipher blocksize length to be used - * @param {String} data - * Plaintext data to be encrypted within the packet - * @return {String} A string representation of the packet - */ - this.write = function(symmetric_algorithm, key, data) { + this.write = function() { return String.fromCharCode(this.version) + this.encrypted; } @@ -89,6 +75,9 @@ function openpgp_packet_sym_encrypted_integrity_protected() { + prefixrandom.charAt(prefixrandom.length - 1) var tohash = bytes; + + + // Modification detection code packet. tohash += String.fromCharCode(0xD3); tohash += String.fromCharCode(0x14); @@ -128,12 +117,12 @@ function openpgp_packet_sym_encrypted_integrity_protected() { util.print_debug_hexstr_dump("calc hash = ", this.hash); - this.data.read(decrypted); - // We pop the mandatory modification detection code packet. - var mdc = this.data.packets.pop(); + this.data.read(decrypted.substr(0, decrypted.length - 22)); - if(this.hash != mdc.hash) { + var mdc = decrypted.substr(decrypted.length - 20, 20); + + if(this.hash != mdc) { this.data = null; util.print_error("Decryption stopped: discovered a " + "modification of encrypted data."); @@ -156,10 +145,3 @@ function openpgp_packet_sym_encrypted_integrity_protected() { } }; - -function openpgp_packet_modification_detection_code() { - this.hash = null; - this.read = function(bytes) { - this.hash = bytes; - } -} diff --git a/src/packet/sym_encrypted_session_key.js b/src/packet/sym_encrypted_session_key.js new file mode 100644 index 00000000..92157538 --- /dev/null +++ b/src/packet/sym_encrypted_session_key.js @@ -0,0 +1,109 @@ +// 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_sym_encrypted_session_key() { + this.tag = 3; + this.private_algorithm = openpgp.symmetric.plaintext; + this.algorithm = openpgp.symmetric.plaintext; + this.encrypted = null; + this.s2k = new openpgp_type_s2k(); + + /** + * 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 + */ + this.read = function(bytes) { + // A one-octet version number. The only currently defined version is 4. + this.version = bytes[0].charCodeAt(); + + // A one-octet number describing the symmetric algorithm used. + this.private_algorithm = bytes[1].charCodeAt(); + + // A string-to-key (S2K) specifier, length as defined above. + this.s2k.read(bytes, 2); + + // Optionally, the encrypted session key itself, which is decrypted + // with the string-to-key object. + var done = this.s2k.length + 2; + if(done < bytes.length) { + this.encrypted = bytes.substr(done); + } + } + /** + * 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 + */ + this.decrypt = function(passphrase) { + var length = openpgp_crypto_getKeyLength(this.private_algorithm); + var key = this.s2k.produce_key(passphrase, length); + + if(this.encrypted == null) { + this.key = key; + this.algorithm = this.private_algorithm; + } else { + var decrypted = openpgp_crypto_symmetricDecrypt( + this.private_algorithm, key, this.encrypted, true); + + this.algorithm = decrypted[0].keyCodeAt(); + this.key = decrypted.substr(1); + } + } + + /** + * Creates a string representation of this object (useful for debug + * purposes) + * + * @return {String} The string containing a openpgp description + */ + this.toString = function() { + 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'; + } +}; + diff --git a/src/packet/symmetrically_encrypted.js b/src/packet/symmetrically_encrypted.js index 2855ed46..490ef9f8 100644 --- a/src/packet/symmetrically_encrypted.js +++ b/src/packet/symmetrically_encrypted.js @@ -30,7 +30,6 @@ function openpgp_packet_symmetrically_encrypted() { this.tag = 9; this.encrypted = null; this.data = new openpgp_packetlist(); - this.algorithm = openpgp.symmetric.plaintext; diff --git a/src/type/openpgp.type.s2k.js b/src/type/openpgp.type.s2k.js index b0d35afa..f08f40c0 100644 --- a/src/type/openpgp.type.s2k.js +++ b/src/type/openpgp.type.s2k.js @@ -88,6 +88,7 @@ function openpgp_type_s2k() { util.print_error("unknown s2k type! "+this.type); break; } + this.packetLength = mypos - position; return this; } @@ -115,10 +116,12 @@ function openpgp_type_s2k() { */ function produce_key(passphrase, numBytes) { passphrase = util.encode_utf8(passphrase); + var result; + if (this.type == 0) { - return openpgp_crypto_hashData(this.hashAlgorithm,passphrase); + result = openpgp_crypto_hashData(this.hashAlgorithm,passphrase); } else if (this.type == 1) { - return openpgp_crypto_hashData(this.hashAlgorithm,this.saltValue+passphrase); + result = openpgp_crypto_hashData(this.hashAlgorithm,this.saltValue+passphrase); } else if (this.type == 3) { var isp = []; isp[0] = this.saltValue+passphrase; @@ -127,12 +130,19 @@ function openpgp_type_s2k() { isp = isp.join(''); if (isp.length > this.count) isp = isp.substr(0, this.count); - if(numBytes && (numBytes == 24 || numBytes == 32)){ //This if accounts for RFC 4880 3.7.1.1 -- If hash size is greater than block size, use leftmost bits. If blocksize larger than hash size, we need to rehash isp and prepend with 0. + if(numBytes && (numBytes == 24 || numBytes == 32)){ + //This if accounts for RFC 4880 3.7.1.1 -- If hash size is + //greater than block size, use leftmost bits. If blocksize + //larger than hash size, we need to rehash isp and prepend with 0. + var key = openpgp_crypto_hashData(this.hashAlgorithm,isp); - return key + openpgp_crypto_hashData(this.hashAlgorithm,String.fromCharCode(0)+isp); + result = key + openpgp_crypto_hashData(this.hashAlgorithm, + String.fromCharCode(0)+isp); } - return openpgp_crypto_hashData(this.hashAlgorithm,isp); + else result = openpgp_crypto_hashData(this.hashAlgorithm,isp); } else return null; + + return result.substr(0, numBytes); } this.read = read; diff --git a/test/general/packet.js b/test/general/packet.js index a658dc70..7a27da2c 100644 --- a/test/general/packet.js +++ b/test/general/packet.js @@ -46,6 +46,35 @@ unittests.register("Packet testing", function() { return new test_result('Sym. encrypted integrity protected data packet', msg2.packets[0].data.packets[0].data == literal.data); + }, function() { + + var msg = + '-----BEGIN PGP MESSAGE-----\n' + + 'Version: GnuPG v2.0.19 (GNU/Linux)\n' + + '\n' + + 'jA0ECQMCpo7I8WqsebTJ0koBmm6/oqdHXJU9aPe+Po+nk/k4/PZrLmlXwz2lhqBg\n' + + 'GAlY9rxVStLBrg0Hn+5gkhyHI9B85rM1BEYXQ8pP5CSFuTwbJ3O2s67dzQ==\n' + + '=VZ0/\n' + + '-----END PGP MESSAGE-----'; + + + + var msgbytes = openpgp_encoding_deArmor(msg).openpgp; + + var parsed = new openpgp_packetlist(); + parsed.read(msgbytes); + + parsed.packets[0].decrypt('test'); + + var key = parsed.packets[0].key; + parsed.packets[1].decrypt(parsed.packets[0].algorithm, key); + var compressed = parsed.packets[1].data.packets[0]; + compressed.decompress(); + + var result = compressed.data.packets[0].data; + + return new test_result('Sym encrypted session key with a compressed packet', + result == 'Hello world!\n'); }]; var results = [];