diff --git a/resources/openpgp.js b/resources/openpgp.js index 7bb32d5f..cc7c5046 100644 --- a/resources/openpgp.js +++ b/resources/openpgp.js @@ -3666,7 +3666,7 @@ function normal_cfb_decrypt(blockcipherencryptfn, block_size, key, ciphertext, i * @param {Integer} algo Algorithm to be used (See RFC4880 9.1) * @param {openpgp_type_mpi[]} publicMPIs Algorithm dependent multiprecision integers * @param {openpgp_type_mpi} data Data to be encrypted as MPI - * @return {(openpgp_type_mpi|openpgp_type_mpi[])} if RSA an openpgp_type_mpi; + * @return {openpgp_type_mpi[]} if RSA an openpgp_type_mpi; * if elgamal encryption an array of two openpgp_type_mpi is returned; otherwise null */ function openpgp_crypto_asymetricEncrypt(algo, publicMPIs, data) { @@ -3678,7 +3678,7 @@ function openpgp_crypto_asymetricEncrypt(algo, publicMPIs, data) { var n = publicMPIs[0].toBigInteger(); var e = publicMPIs[1].toBigInteger(); var m = data.toBigInteger(); - return rsa.encrypt(m,e,n).toMPI(); + return [rsa.encrypt(m,e,n)]; case 16: // Elgamal (Encrypt-Only) [ELGAMAL] [HAC] var elgamal = new Elgamal(); var p = publicMPIs[0].toBigInteger(); @@ -7391,7 +7391,7 @@ function openpgp_config() { keyserver: "keyserver.linux.it" // "pgp.mit.edu:11371" }; - this.versionstring ="OpenPGP.js v.1.20130423"; + this.versionstring ="OpenPGP.js v.1.20130424"; this.commentstring ="http://openpgpjs.org"; /** * Reads the config out of the HTML5 local storage @@ -12379,6 +12379,19 @@ function _openpgp_packet() { sym_encrypted_integrity_protected: 18, modification_detection_code: 19 }; + + /* + + TODO Invoke this code instead of putting a tag variable + inside each and every packet class. Right now we don't + know whether or not they have been loaded yet. + + for(var i in this.type) { + var classname = 'openpgp_packet_' + i; + window[classname].prototype.tag = this.type[i]; + } + + */ } var openpgp_packet = new _openpgp_packet(); @@ -12451,6 +12464,218 @@ function openpgp_packetlist() { // 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_public_key_encrypted_session_key() { + this.tag = 1; + this.version = 3; + + this.public_key_id = new openpgp_type_keyid(); + this.public_key_algorithm = openpgp.publickey.rsa_encrypt_sign; + + this.symmetric_key = null; + this.symmetric_algorithm = openpgp.symmetric.plaintext; + + /** @type {openpgp_type_mpi[]} */ + this.encrypted = []; + + /** + * 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 + */ + this.read = function(bytes) { + if (bytes.length < 10) { + util.print_error("openpgp.packet.encryptedsessionkey.js\n" + 'invalid length'); + return null; + } + + this.version = bytes[0].charCodeAt(); + + this.public_key_id.read_packet(bytes, 1); + + this.public_key_algorithm = bytes[9].charCodeAt(); + + var i = 10; + + switch (this.public_key_algorithm) { + + case openpgp.publickey.rsa_encrypt: + case openpgp.publickey.rsa_encrypt_sign: + this.encrypted = []; + this.encrypted[0] = new openpgp_type_mpi(); + this.encrypted[0].read(bytes, i, bytes.length - i); + break; + + case openpgp.publickey.elgamal: + this.encrypted = []; + this.encrypted[0] = new openpgp_type_mpi(); + this.encrypted[0].read(bytes, i, bytes.length - i); + i += this.encrypted[0].packetLength; + this.encrypted[1] = new openpgp_type_mpi(); + this.encrypted[1].read(bytes, i, bytes.length - i); + break; + + default: + util.print_error("openpgp.packet.encryptedsessionkey.js\n" + + "unknown public key packet algorithm type " + + this.public_key_algorithm); + break; + } + } + + /** + * 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 + */ + this.write = function() { + + var result = String.fromCharCode(this.version); + result += this.public_key_id.bytes; + result += String.fromCharCode(this.public_key_algorithm); + + for ( var i = 0; i < this.encrypted.length; i++) { + result += this.encrypted[i].toBin(); + } + + return result; + } + + this.encrypt = function(public_key_mpi) { + + var data = String.fromCharCode(this.symmetric_algorithm); + data += this.symmetric_key; + var checksum = util.calc_checksum(this.symmetric_key); + data += String.fromCharCode((checksum >> 8) & 0xFF); + data += String.fromCharCode((checksum) & 0xFF); + + var mpi = new openpgp_type_mpi(); + + var encrypted = openpgp_crypto_asymetricEncrypt( + this.public_key_algorithm, + public_key_mpi, + mpi.create(openpgp_encoding_eme_pkcs1_encode(data, + public_key_mpi[0].mpiByteLength))); + + // TODO: fix this - make openpgp_crypto_ interfaces uniform + this.encrypted = encrypted.map(function(k) { + var mpi = new openpgp_type_mpi; + var b = k.toMPI(); + mpi.read(b, 0, b.length); + return mpi; + }); + } + + /** + * 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(public_key_mpi, private_key_mpi) { + var result = openpgp_crypto_asymetricDecrypt( + this.public_key_algorithm, + public_key_mpi, + private_key_mpi, + this.encrypted).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), + public_key_mpi[0].getByteLength()); + + var key = decoded.substring(1); + + if(checksum != util.calc_checksum(key)) { + util.print_error("Checksum mismatch"); + } + else { + this.symmetric_key = key; + this.symmetric_algorithm = decoded.charCodeAt(0); + } + } + + /** + * Creates a string representation of this object (useful for debug + * purposes) + * + * @return {String} The string containing a openpgp description + */ + this.toString = function() { + 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.encrypted.length; i++) { + result += this.encrypted[i].toString(); + } + return result; + } +}; + +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + /** * @class * @classdesc Implementation of the Sym. Encrypted Integrity Protected Data @@ -12475,8 +12700,6 @@ function openpgp_packet_sym_encrypted_integrity_protected() { */ this.modification = false; this.packets = new openpgp_packetlist(); - /** @type {openpgp.symmetric} */ - this.algorithm = openpgp.symmetric.plaintext; this.read = function(bytes) { @@ -13322,6 +13545,8 @@ var Util = function() { * containing the HTML encoded error message */ this.print_error = function(str) { + if(this.debug) + throw str; console.log(str); }; @@ -13335,7 +13560,8 @@ var Util = function() { * containing the HTML encoded info message */ this.print_info = function(str) { - console.log(str); + if(this.debug) + console.log(str); }; this.print_warning = function(str) { diff --git a/resources/openpgp.min.js b/resources/openpgp.min.js index 7d3181e8..34b9e4d4 100644 --- a/resources/openpgp.min.js +++ b/resources/openpgp.min.js @@ -107,7 +107,7 @@ function openpgp_cfb_decrypt(b,a,c,d,e){util.print_debug("resync:"+e);var f=Arra k.join("");if(e){for(h=0;ha*g;){for(var e=b(f,c),f=d.substring(g*a,g*a+a),k=0;ka*g;){for(var j=b(f,c),f=d.substring(g*a+0,g*a+a+0),e=0;ed?(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.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", +function openpgp_config(){this.config=null;this.default_config={prefer_hash_algorithm:8,encryption_cipher:9,compression:1,show_version:!0,show_comment:!0,integrity_protect:!0,composition_behavior:0,keyserver:"keyserver.linux.it"};this.versionstring="OpenPGP.js v.1.20130424";this.commentstring="http://openpgpjs.org";this.debug=!1;this.read=function(){var b=JSON.parse(window.localStorage.getItem("config"));null==b?(this.config=this.default_config,this.write()):this.config=b};this.write=function(){window.localStorage.setItem("config", 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<a[g].charCodeAt()){j=a[g++].charCodeA g+j);d+=j;g+=j;break}j=g}else b++,d=a[b++].charCodeAt()<<24|a[b++].charCodeAt()<<16|a[b++].charCodeAt()<<8|a[b++].charCodeAt();else switch(g){case 0:d=a[b++].charCodeAt();break;case 1:d=a[b++].charCodeAt()<<8|a[b++].charCodeAt();break;case 2:d=a[b++].charCodeAt()<<24|a[b++].charCodeAt()<<16|a[b++].charCodeAt()<<8|a[b++].charCodeAt();break}-1==j&&(j=d);null==h&&(h=a.substring(b,b+j));var a={},k;for(k in this.type)a[this.type[k]]=k;k="openpgp_packet_"+a[e];a=window[k];if(void 0==a)throw k;k=new a;k.read(h); return{packet:k,offset:b+d}};this.type={reserved:0,public_key_encrypted_session_key:1,signature:2,sym_encrypted_session_key:3,one_pass_signature:4,secret_key:5,public_key:6,secret_subkey:7,compressed:8,symmetrically_encrypted:9,marker:10,literal:11,trust:12,userid:13,public_subkey:14,user_attribute:17,sym_encrypted_integrity_protected:18,modification_detection_code:19}}var openpgp_packet=new _openpgp_packet; function openpgp_packetlist(){this.length=0;this.read=function(b){this.packets=[];for(var a=0;ab.length)return util.print_error("openpgp.packet.encryptedsessionkey.js\ninvalid length"),null;this.version=b[0].charCodeAt();this.public_key_id.read_packet(b,1);this.public_key_algorithm=b[9].charCodeAt(); +var a=10;switch(this.public_key_algorithm){case openpgp.publickey.rsa_encrypt:case openpgp.publickey.rsa_encrypt_sign:this.encrypted=[];this.encrypted[0]=new openpgp_type_mpi;this.encrypted[0].read(b,a,b.length-a);break;case openpgp.publickey.elgamal:this.encrypted=[];this.encrypted[0]=new openpgp_type_mpi;this.encrypted[0].read(b,a,b.length-a);a+=this.encrypted[0].packetLength;this.encrypted[1]=new openpgp_type_mpi;this.encrypted[1].read(b,a,b.length-a);break;default:util.print_error("openpgp.packet.encryptedsessionkey.js\nunknown public key packet algorithm type "+ +this.public_key_algorithm)}};this.write=function(){for(var b=String.fromCharCode(this.version),b=b+this.public_key_id.bytes,b=b+String.fromCharCode(this.public_key_algorithm),a=0;a>8&255),a=a+String.fromCharCode(c&255),c=new openpgp_type_mpi;this.encrypted=openpgp_crypto_asymetricEncrypt(this.public_key_algorithm, +b,c.create(openpgp_encoding_eme_pkcs1_encode(a,b[0].mpiByteLength))).map(function(a){var b=new openpgp_type_mpi,a=a.toMPI();b.read(a,0,a.length);return b})};this.decrypt=function(b,a){var c=openpgp_crypto_asymetricDecrypt(this.public_key_algorithm,b,a,this.encrypted).toMPI(),d=(c.charCodeAt(c.length-2)<<8)+c.charCodeAt(c.length-1),c=openpgp_encoding_eme_pkcs1_decode(c.substring(2,c.length-2),b[0].getByteLength()),e=c.substring(1);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;ag.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>=b%8,0>=b%8,0> 8) & 0xFF); + data += String.fromCharCode((checksum) & 0xFF); + + var mpi = new openpgp_type_mpi(); + + var encrypted = openpgp_crypto_asymetricEncrypt( + this.public_key_algorithm, + public_key_mpi, + mpi.create(openpgp_encoding_eme_pkcs1_encode(data, + public_key_mpi[0].mpiByteLength))); + + // TODO: fix this - make openpgp_crypto_ interfaces uniform + this.encrypted = encrypted.map(function(k) { + var mpi = new openpgp_type_mpi; + var b = k.toMPI(); + mpi.read(b, 0, b.length); + return mpi; + }); + } + + /** + * 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(public_key_mpi, private_key_mpi) { + var result = openpgp_crypto_asymetricDecrypt( + this.public_key_algorithm, + public_key_mpi, + private_key_mpi, + this.encrypted).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), + public_key_mpi[0].getByteLength()); + + var key = decoded.substring(1); + + if(checksum != util.calc_checksum(key)) { + util.print_error("Checksum mismatch"); + } + else { + this.symmetric_key = key; + this.symmetric_algorithm = decoded.charCodeAt(0); + } + } + + /** + * Creates a string representation of this object (useful for debug + * purposes) + * + * @return {String} The string containing a openpgp description + */ + this.toString = function() { + 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.encrypted.length; i++) { + result += this.encrypted[i].toString(); + } + return result; + } +}; + diff --git a/src/packet/sym_encrypted_integrity_protected.js b/src/packet/sym_encrypted_integrity_protected.js index 6bfc90f9..220158f2 100644 --- a/src/packet/sym_encrypted_integrity_protected.js +++ b/src/packet/sym_encrypted_integrity_protected.js @@ -39,8 +39,6 @@ function openpgp_packet_sym_encrypted_integrity_protected() { */ this.modification = false; this.packets = new openpgp_packetlist(); - /** @type {openpgp.symmetric} */ - this.algorithm = openpgp.symmetric.plaintext; this.read = function(bytes) { diff --git a/src/util/util.js b/src/util/util.js index 26effa68..706eb01f 100644 --- a/src/util/util.js +++ b/src/util/util.js @@ -220,6 +220,8 @@ var Util = function() { * containing the HTML encoded error message */ this.print_error = function(str) { + if(this.debug) + throw str; console.log(str); }; @@ -233,7 +235,8 @@ var Util = function() { * containing the HTML encoded info message */ this.print_info = function(str) { - console.log(str); + if(this.debug) + console.log(str); }; this.print_warning = function(str) { diff --git a/test/general/packet.js b/test/general/packet.js index c6f1e4bd..682d1cbf 100644 --- a/test/general/packet.js +++ b/test/general/packet.js @@ -75,6 +75,42 @@ unittests.register("Packet testing", function() { return new test_result('Sym encrypted session key with a compressed packet', result == 'Hello world!\n'); + + }, function() { + + var rsa = new RSA(), + key = rsa.generate(512, "10001") + + + var key = [[key.d.toMPI(), key.p.toMPI(), key.q.toMPI(), key.u.toMPI()], + [key.n.toMPI(), key.ee.toMPI()]]; + + key = key.map(function(k) { + return k.map(function(v) { + var mpi = new openpgp_type_mpi(); + mpi.read(v, 0, v.length); + return mpi; + }); + }); + var enc = new openpgp_packet_public_key_encrypted_session_key(), + msg = new openpgp_packetlist(), + msg2 = new openpgp_packetlist(); + + enc.symmetric_key = '12345678901234567890123456789012', + enc.public_key_algorithm = openpgp.publickey.rsa_encrypt; + enc.symmetric_algorithm = openpgp.symmetric.aes256; + enc.public_key_id.bytes = '12345678'; + enc.encrypt(key[1]); + + msg.push(enc); + + msg2.read(msg.write()); + + msg2[0].decrypt(key[1], key[0]); + + return new test_result('Public key encrypted symmetric key packet', + msg2[0].symmetric_key == enc.symmetric_key && + msg2[0].symmetric_algorithm == enc.symmetric_algorithm); }]; var results = []; @@ -85,4 +121,4 @@ unittests.register("Packet testing", function() { return results; -}); +})