From 530a9a0c698d03dc6862e5a7f0b3f9196860ed40 Mon Sep 17 00:00:00 2001 From: Michal Kolodziej Date: Wed, 24 Apr 2013 10:46:43 +0200 Subject: [PATCH] Changed mpi interface to a more sane variant - openpgp methods MUST return openpgp_type_mpi, using BigIntegers is prohibited as otherwise the wrapper has no point. --- resources/openpgp.js | 380 +++++++++--------- resources/openpgp.min.js | 18 +- src/ciphers/openpgp.crypto.js | 97 +++-- .../public_key_encrypted_session_key.js | 39 +- src/type/{openpgp.type.mpi.js => mpi.js} | 106 ++--- src/type/openpgp.type.keyid.js | 4 + test/general/packet.js | 20 +- 7 files changed, 315 insertions(+), 349 deletions(-) rename src/type/{openpgp.type.mpi.js => mpi.js} (61%) diff --git a/resources/openpgp.js b/resources/openpgp.js index cc7c5046..92261658 100644 --- a/resources/openpgp.js +++ b/resources/openpgp.js @@ -3670,25 +3670,33 @@ function normal_cfb_decrypt(blockcipherencryptfn, block_size, key, ciphertext, i * if elgamal encryption an array of two openpgp_type_mpi is returned; otherwise null */ function openpgp_crypto_asymetricEncrypt(algo, publicMPIs, data) { - switch(algo) { - case 1: // RSA (Encrypt or Sign) [HAC] - case 2: // RSA Encrypt-Only [HAC] - case 3: // RSA Sign-Only [HAC] - var rsa = new RSA(); - var n = publicMPIs[0].toBigInteger(); - var e = publicMPIs[1].toBigInteger(); - var m = data.toBigInteger(); - return [rsa.encrypt(m,e,n)]; - case 16: // Elgamal (Encrypt-Only) [ELGAMAL] [HAC] - var elgamal = new Elgamal(); - var p = publicMPIs[0].toBigInteger(); - var g = publicMPIs[1].toBigInteger(); - var y = publicMPIs[2].toBigInteger(); - var m = data.toBigInteger(); - return elgamal.encrypt(m,g,p,y); - default: - return null; - } + var result = (function() { + switch(algo) { + case 1: // RSA (Encrypt or Sign) [HAC] + case 2: // RSA Encrypt-Only [HAC] + case 3: // RSA Sign-Only [HAC] + var rsa = new RSA(); + var n = publicMPIs[0].toBigInteger(); + var e = publicMPIs[1].toBigInteger(); + var m = data.toBigInteger(); + return [rsa.encrypt(m,e,n)]; + case 16: // Elgamal (Encrypt-Only) [ELGAMAL] [HAC] + var elgamal = new Elgamal(); + var p = publicMPIs[0].toBigInteger(); + var g = publicMPIs[1].toBigInteger(); + var y = publicMPIs[2].toBigInteger(); + var m = data.toBigInteger(); + return elgamal.encrypt(m,g,p,y); + default: + return []; + } + })(); + + return result.map(function(bn) { + var mpi = new openpgp_type_mpi(); + mpi.fromBigInteger(bn); + return mpi; + }); } /** @@ -3700,32 +3708,37 @@ function openpgp_crypto_asymetricEncrypt(algo, publicMPIs, data) { * @param {openpgp_type_mpi[]} secretMPIs Algorithm dependent multiprecision integers * of the private key used * @param {openpgp_type_mpi} data Data to be encrypted as MPI - * @return {BigInteger} returns a big integer containing the decrypted data; otherwise null + * @return {openpgp_type_mpi} returns a big integer containing the decrypted data; otherwise null */ function openpgp_crypto_asymetricDecrypt(algo, publicMPIs, secretMPIs, dataMPIs) { - switch(algo) { - case 1: // RSA (Encrypt or Sign) [HAC] - case 2: // RSA Encrypt-Only [HAC] - case 3: // RSA Sign-Only [HAC] - var rsa = new RSA(); - var d = secretMPIs[0].toBigInteger(); - var p = secretMPIs[1].toBigInteger(); - var q = secretMPIs[2].toBigInteger(); - var u = secretMPIs[3].toBigInteger(); - var m = dataMPIs[0].toBigInteger(); - return rsa.decrypt(m, d, p, q, u); - case 16: // Elgamal (Encrypt-Only) [ELGAMAL] [HAC] - var elgamal = new Elgamal(); - var x = secretMPIs[0].toBigInteger(); - var c1 = dataMPIs[0].toBigInteger(); - var c2 = dataMPIs[1].toBigInteger(); - var p = publicMPIs[0].toBigInteger(); - return elgamal.decrypt(c1,c2,p,x); - default: - return null; - } - + var bn = (function() { + switch(algo) { + case 1: // RSA (Encrypt or Sign) [HAC] + case 2: // RSA Encrypt-Only [HAC] + case 3: // RSA Sign-Only [HAC] + var rsa = new RSA(); + var d = secretMPIs[0].toBigInteger(); + var p = secretMPIs[1].toBigInteger(); + var q = secretMPIs[2].toBigInteger(); + var u = secretMPIs[3].toBigInteger(); + var m = dataMPIs[0].toBigInteger(); + return rsa.decrypt(m, d, p, q, u); + case 16: // Elgamal (Encrypt-Only) [ELGAMAL] [HAC] + var elgamal = new Elgamal(); + var x = secretMPIs[0].toBigInteger(); + var c1 = dataMPIs[0].toBigInteger(); + var c2 = dataMPIs[1].toBigInteger(); + var p = publicMPIs[0].toBigInteger(); + return elgamal.decrypt(c1,c2,p,x); + default: + return null; + } + })(); + + var result = new openpgp_type_mpi(); + result.fromBigInteger(bn); + return result; } /** @@ -12509,9 +12522,7 @@ function openpgp_packet_public_key_encrypted_session_key() { } this.version = bytes[0].charCodeAt(); - this.public_key_id.read_packet(bytes, 1); - this.public_key_algorithm = bytes[9].charCodeAt(); var i = 10; @@ -12522,16 +12533,15 @@ function openpgp_packet_public_key_encrypted_session_key() { case openpgp.publickey.rsa_encrypt_sign: this.encrypted = []; this.encrypted[0] = new openpgp_type_mpi(); - this.encrypted[0].read(bytes, i, bytes.length - i); + this.encrypted[0].read(bytes.substr(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; + i += this.encrypted[0].read(bytes.substr(i)); this.encrypted[1] = new openpgp_type_mpi(); - this.encrypted[1].read(bytes, i, bytes.length - i); + this.encrypted[1].read(bytes.substr(i)); break; default: @@ -12567,7 +12577,7 @@ function openpgp_packet_public_key_encrypted_session_key() { result += String.fromCharCode(this.public_key_algorithm); for ( var i = 0; i < this.encrypted.length; i++) { - result += this.encrypted[i].toBin(); + result += this.encrypted[i].write() } return result; @@ -12582,20 +12592,14 @@ function openpgp_packet_public_key_encrypted_session_key() { data += String.fromCharCode((checksum) & 0xFF); var mpi = new openpgp_type_mpi(); + mpi.fromBytes(openpgp_encoding_eme_pkcs1_encode( + data, + public_key_mpi[0].byteLength())); - var encrypted = openpgp_crypto_asymetricEncrypt( + this.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; - }); + mpi); } /** @@ -12613,16 +12617,16 @@ function openpgp_packet_public_key_encrypted_session_key() { this.public_key_algorithm, public_key_mpi, private_key_mpi, - this.encrypted).toMPI(); + this.encrypted).toBytes(); - var checksum = ((result.charCodeAt(result.length - 2) << 8) + result - .charCodeAt(result.length - 1)); + 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()); + result, + public_key_mpi[0].byteLength()); - var key = decoded.substring(1); + var key = decoded.substring(1, decoded.length - 2); if(checksum != util.calc_checksum(key)) { util.print_error("Checksum mismatch"); @@ -13005,6 +13009,111 @@ 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 +// Hint: We hold our MPIs as an array of octets in big endian format preceeding a two +// octet scalar: MPI: [a,b,c,d,e,f] +// - MPI size: (a << 8) | b +// - MPI = c | d << 8 | e << ((MPI.length -2)*8) | f ((MPI.length -2)*8) + +/** + * @class + * @classdescImplementation of type MPI (RFC4880 3.2) + * Multiprecision integers (also called MPIs) are unsigned integers used + * to hold large integers such as the ones used in cryptographic + * calculations. + * An MPI consists of two pieces: a two-octet scalar that is the length + * of the MPI in bits followed by a string of octets that contain the + * actual integer. + */ +function openpgp_type_mpi() { + /** An implementation dependent integer */ + this.data = null; + + /** + * Parsing function for a mpi (RFC 4880 3.2). + * @param {String} input Payload of mpi data + * @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_type_mpi} Object representation + */ + this.read = function(bytes) { + var bits = (bytes[0].charCodeAt() << 8) | bytes[1].charCodeAt(); + + // Additional rules: + // + // The size of an MPI is ((MPI.length + 7) / 8) + 2 octets. + // + // The length field of an MPI describes the length starting from its + // most significant non-zero bit. Thus, the MPI [00 02 01] is not + // formed correctly. It should be [00 01 01]. + + // TODO: Verification of this size method! This size calculation as + // specified above is not applicable in JavaScript + var bytelen = Math.ceil(bits / 8); + + var raw = bytes.substr(2, bytelen); + this.fromBytes(raw); + + return 2 + bytelen; + } + + this.fromBytes = function(bytes) { + this.data = new BigInteger(util.hexstrdump(bytes), 16); + } + + this.toBytes = function() { + return this.write().substr(2); + } + + this.byteLength = function() { + return this.toBytes().length; + } + + /** + * Converts the mpi object to a string as specified in RFC4880 3.2 + * @return {String} mpi Byte representation + */ + this.write = function() { + return this.data.toMPI(); + } + + this.toBigInteger = function() { + return this.data.clone(); + } + + this.fromBigInteger = function(bn) { + this.data = bn.clone(); + } + + /** + * Generates debug output (pretty print) + * @return {String} String which gives some information about the mpi + */ + this.toString = function() { + var r = " MPI("+this.mpiBitLength+"b/"+this.mpiByteLength+"B) : 0x"; + r+=util.hexstrdump(this.MPI); + return r+'\n'; + } +} + +// 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 type key id (RFC4880 3.3) @@ -13014,6 +13123,10 @@ function openpgp_packet_symmetrically_encrypted() { formed. */ function openpgp_type_keyid() { + var bytes = ''; + + for(var i = 0; i < 8; i++) + bytes += String.fromCharCode(0); /** * Parsing method for a key id * @param {String} input Input to read the key id from @@ -13054,141 +13167,6 @@ function openpgp_type_keyid() { // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -// Hint: We hold our MPIs as an array of octets in big endian format preceeding a two -// octet scalar: MPI: [a,b,c,d,e,f] -// - MPI size: (a << 8) | b -// - MPI = c | d << 8 | e << ((MPI.length -2)*8) | f ((MPI.length -2)*8) - -/** - * @class - * @classdescImplementation of type MPI (RFC4880 3.2) - * Multiprecision integers (also called MPIs) are unsigned integers used - * to hold large integers such as the ones used in cryptographic - * calculations. - * An MPI consists of two pieces: a two-octet scalar that is the length - * of the MPI in bits followed by a string of octets that contain the - * actual integer. - */ -function openpgp_type_mpi() { - this.MPI = null; - this.mpiBitLength = null; - this.mpiByteLength = null; - this.data = null; - /** - * Parsing function for a mpi (RFC 4880 3.2). - * @param {String} input Payload of mpi data - * @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_type_mpi} Object representation - */ - function read(input, position, len) { - var mypos = position; - - this.mpiBitLength = (input[mypos++].charCodeAt() << 8) | input[mypos++].charCodeAt(); - - // Additional rules: - // - // The size of an MPI is ((MPI.length + 7) / 8) + 2 octets. - // - // The length field of an MPI describes the length starting from its - // most significant non-zero bit. Thus, the MPI [00 02 01] is not - // formed correctly. It should be [00 01 01]. - - // TODO: Verification of this size method! This size calculation as - // specified above is not applicable in JavaScript - this.mpiByteLength = (this.mpiBitLength - (this.mpiBitLength % 8)) / 8; - if (this.mpiBitLength % 8 != 0) - this.mpiByteLength++; - - this.MPI = input.substring(mypos,mypos+this.mpiByteLength); - this.data = input.substring(position, position+2+this.mpiByteLength); - this.packetLength = this.mpiByteLength +2; - return this; - } - - /** - * Generates debug output (pretty print) - * @return {String} String which gives some information about the mpi - */ - function toString() { - var r = " MPI("+this.mpiBitLength+"b/"+this.mpiByteLength+"B) : 0x"; - r+=util.hexstrdump(this.MPI); - return r+'\n'; - } - - /** - * Converts the mpi to an BigInteger object - * @return {BigInteger} - */ - function getBigInteger() { - return new BigInteger(util.hexstrdump(this.MPI),16); - } - - - function getBits(num) { - for (var i = 0; i < 9; i++) - if (num >> i == 0) - return i; - } - - /** - * Gets the length of the mpi in bytes - * @return {Integer} Mpi byte length - */ - function getByteLength() { - return this.mpiByteLength; - } - - /** - * Creates an mpi from the specified string - * @param {String} data Data to read the mpi from - * @return {openpgp_type_mpi} - */ - function create(data) { - this.MPI = data; - this.mpiBitLength = (data.length -1) *8 + getBits(data.charCodeAt(0)); - this.mpiByteLength = data.length; - return this; - } - - /** - * Converts the mpi object to a string as specified in RFC4880 3.2 - * @return {String} mpi Byte representation - */ - function toBin() { - var result = String.fromCharCode((this.mpiBitLength >> 8) & 0xFF); - result += String.fromCharCode(this.mpiBitLength & 0xFF); - result += this.MPI; - return result; - } - - this.read = read; - this.toBigInteger = getBigInteger; - this.toString = toString; - this.create = create; - this.toBin = toBin; - this.getByteLength = getByteLength; -} - -// 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 String-to-key specifier (RFC4880 3.7) diff --git a/resources/openpgp.min.js b/resources/openpgp.min.js index 34b9e4d4..778c952c 100644 --- a/resources/openpgp.min.js +++ b/resources/openpgp.min.js @@ -107,8 +107,8 @@ 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;eb.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;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;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}} +this.algorithmType+"\n encrypted data: Bytes ["+util.hexstrdump(this.encryptedData)+"]\n"}} +function openpgp_type_mpi(){this.data=null;this.read=function(b){var a=b[0].charCodeAt()<<8|b[1].charCodeAt(),a=Math.ceil(a/8);this.fromBytes(b.substr(2,a));return 2+a};this.fromBytes=function(b){this.data=new BigInteger(util.hexstrdump(b),16)};this.toBytes=function(){return this.write().substr(2)};this.byteLength=function(){return this.toBytes().length};this.write=function(){return this.data.toMPI()};this.toBigInteger=function(){return this.data.clone()};this.fromBigInteger=function(b){this.data= +b.clone()};this.toString=function(){var b=" MPI("+this.mpiBitLength+"b/"+this.mpiByteLength+"B) : 0x",b=b+util.hexstrdump(this.MPI);return b+"\n"}}function openpgp_type_keyid(){for(var b="",a=0;8>a;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+ 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, diff --git a/src/ciphers/openpgp.crypto.js b/src/ciphers/openpgp.crypto.js index ad1deb3b..656ec4c9 100644 --- a/src/ciphers/openpgp.crypto.js +++ b/src/ciphers/openpgp.crypto.js @@ -27,25 +27,33 @@ * if elgamal encryption an array of two openpgp_type_mpi is returned; otherwise null */ function openpgp_crypto_asymetricEncrypt(algo, publicMPIs, data) { - switch(algo) { - case 1: // RSA (Encrypt or Sign) [HAC] - case 2: // RSA Encrypt-Only [HAC] - case 3: // RSA Sign-Only [HAC] - var rsa = new RSA(); - var n = publicMPIs[0].toBigInteger(); - var e = publicMPIs[1].toBigInteger(); - var m = data.toBigInteger(); - return [rsa.encrypt(m,e,n)]; - case 16: // Elgamal (Encrypt-Only) [ELGAMAL] [HAC] - var elgamal = new Elgamal(); - var p = publicMPIs[0].toBigInteger(); - var g = publicMPIs[1].toBigInteger(); - var y = publicMPIs[2].toBigInteger(); - var m = data.toBigInteger(); - return elgamal.encrypt(m,g,p,y); - default: - return null; - } + var result = (function() { + switch(algo) { + case 1: // RSA (Encrypt or Sign) [HAC] + case 2: // RSA Encrypt-Only [HAC] + case 3: // RSA Sign-Only [HAC] + var rsa = new RSA(); + var n = publicMPIs[0].toBigInteger(); + var e = publicMPIs[1].toBigInteger(); + var m = data.toBigInteger(); + return [rsa.encrypt(m,e,n)]; + case 16: // Elgamal (Encrypt-Only) [ELGAMAL] [HAC] + var elgamal = new Elgamal(); + var p = publicMPIs[0].toBigInteger(); + var g = publicMPIs[1].toBigInteger(); + var y = publicMPIs[2].toBigInteger(); + var m = data.toBigInteger(); + return elgamal.encrypt(m,g,p,y); + default: + return []; + } + })(); + + return result.map(function(bn) { + var mpi = new openpgp_type_mpi(); + mpi.fromBigInteger(bn); + return mpi; + }); } /** @@ -57,32 +65,37 @@ function openpgp_crypto_asymetricEncrypt(algo, publicMPIs, data) { * @param {openpgp_type_mpi[]} secretMPIs Algorithm dependent multiprecision integers * of the private key used * @param {openpgp_type_mpi} data Data to be encrypted as MPI - * @return {BigInteger} returns a big integer containing the decrypted data; otherwise null + * @return {openpgp_type_mpi} returns a big integer containing the decrypted data; otherwise null */ function openpgp_crypto_asymetricDecrypt(algo, publicMPIs, secretMPIs, dataMPIs) { - switch(algo) { - case 1: // RSA (Encrypt or Sign) [HAC] - case 2: // RSA Encrypt-Only [HAC] - case 3: // RSA Sign-Only [HAC] - var rsa = new RSA(); - var d = secretMPIs[0].toBigInteger(); - var p = secretMPIs[1].toBigInteger(); - var q = secretMPIs[2].toBigInteger(); - var u = secretMPIs[3].toBigInteger(); - var m = dataMPIs[0].toBigInteger(); - return rsa.decrypt(m, d, p, q, u); - case 16: // Elgamal (Encrypt-Only) [ELGAMAL] [HAC] - var elgamal = new Elgamal(); - var x = secretMPIs[0].toBigInteger(); - var c1 = dataMPIs[0].toBigInteger(); - var c2 = dataMPIs[1].toBigInteger(); - var p = publicMPIs[0].toBigInteger(); - return elgamal.decrypt(c1,c2,p,x); - default: - return null; - } - + var bn = (function() { + switch(algo) { + case 1: // RSA (Encrypt or Sign) [HAC] + case 2: // RSA Encrypt-Only [HAC] + case 3: // RSA Sign-Only [HAC] + var rsa = new RSA(); + var d = secretMPIs[0].toBigInteger(); + var p = secretMPIs[1].toBigInteger(); + var q = secretMPIs[2].toBigInteger(); + var u = secretMPIs[3].toBigInteger(); + var m = dataMPIs[0].toBigInteger(); + return rsa.decrypt(m, d, p, q, u); + case 16: // Elgamal (Encrypt-Only) [ELGAMAL] [HAC] + var elgamal = new Elgamal(); + var x = secretMPIs[0].toBigInteger(); + var c1 = dataMPIs[0].toBigInteger(); + var c2 = dataMPIs[1].toBigInteger(); + var p = publicMPIs[0].toBigInteger(); + return elgamal.decrypt(c1,c2,p,x); + default: + return null; + } + })(); + + var result = new openpgp_type_mpi(); + result.fromBigInteger(bn); + return result; } /** diff --git a/src/packet/public_key_encrypted_session_key.js b/src/packet/public_key_encrypted_session_key.js index e90b982c..b3e0ba70 100644 --- a/src/packet/public_key_encrypted_session_key.js +++ b/src/packet/public_key_encrypted_session_key.js @@ -60,9 +60,7 @@ function openpgp_packet_public_key_encrypted_session_key() { } this.version = bytes[0].charCodeAt(); - this.public_key_id.read_packet(bytes, 1); - this.public_key_algorithm = bytes[9].charCodeAt(); var i = 10; @@ -73,16 +71,15 @@ function openpgp_packet_public_key_encrypted_session_key() { case openpgp.publickey.rsa_encrypt_sign: this.encrypted = []; this.encrypted[0] = new openpgp_type_mpi(); - this.encrypted[0].read(bytes, i, bytes.length - i); + this.encrypted[0].read(bytes.substr(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; + i += this.encrypted[0].read(bytes.substr(i)); this.encrypted[1] = new openpgp_type_mpi(); - this.encrypted[1].read(bytes, i, bytes.length - i); + this.encrypted[1].read(bytes.substr(i)); break; default: @@ -118,7 +115,7 @@ function openpgp_packet_public_key_encrypted_session_key() { result += String.fromCharCode(this.public_key_algorithm); for ( var i = 0; i < this.encrypted.length; i++) { - result += this.encrypted[i].toBin(); + result += this.encrypted[i].write() } return result; @@ -133,20 +130,14 @@ function openpgp_packet_public_key_encrypted_session_key() { data += String.fromCharCode((checksum) & 0xFF); var mpi = new openpgp_type_mpi(); + mpi.fromBytes(openpgp_encoding_eme_pkcs1_encode( + data, + public_key_mpi[0].byteLength())); - var encrypted = openpgp_crypto_asymetricEncrypt( + this.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; - }); + mpi); } /** @@ -164,16 +155,16 @@ function openpgp_packet_public_key_encrypted_session_key() { this.public_key_algorithm, public_key_mpi, private_key_mpi, - this.encrypted).toMPI(); + this.encrypted).toBytes(); - var checksum = ((result.charCodeAt(result.length - 2) << 8) + result - .charCodeAt(result.length - 1)); + 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()); + result, + public_key_mpi[0].byteLength()); - var key = decoded.substring(1); + var key = decoded.substring(1, decoded.length - 2); if(checksum != util.calc_checksum(key)) { util.print_error("Checksum mismatch"); diff --git a/src/type/openpgp.type.mpi.js b/src/type/mpi.js similarity index 61% rename from src/type/openpgp.type.mpi.js rename to src/type/mpi.js index 82f56690..2b019a2b 100644 --- a/src/type/openpgp.type.mpi.js +++ b/src/type/mpi.js @@ -31,10 +31,9 @@ * actual integer. */ function openpgp_type_mpi() { - this.MPI = null; - this.mpiBitLength = null; - this.mpiByteLength = null; + /** An implementation dependent integer */ this.data = null; + /** * Parsing function for a mpi (RFC 4880 3.2). * @param {String} input Payload of mpi data @@ -44,10 +43,8 @@ function openpgp_type_mpi() { * input at position * @return {openpgp_type_mpi} Object representation */ - function read(input, position, len) { - var mypos = position; - - this.mpiBitLength = (input[mypos++].charCodeAt() << 8) | input[mypos++].charCodeAt(); + this.read = function(bytes) { + var bits = (bytes[0].charCodeAt() << 8) | bytes[1].charCodeAt(); // Additional rules: // @@ -59,77 +56,50 @@ function openpgp_type_mpi() { // TODO: Verification of this size method! This size calculation as // specified above is not applicable in JavaScript - this.mpiByteLength = (this.mpiBitLength - (this.mpiBitLength % 8)) / 8; - if (this.mpiBitLength % 8 != 0) - this.mpiByteLength++; + var bytelen = Math.ceil(bits / 8); - this.MPI = input.substring(mypos,mypos+this.mpiByteLength); - this.data = input.substring(position, position+2+this.mpiByteLength); - this.packetLength = this.mpiByteLength +2; - return this; - } - - /** - * Generates debug output (pretty print) - * @return {String} String which gives some information about the mpi - */ - function toString() { - var r = " MPI("+this.mpiBitLength+"b/"+this.mpiByteLength+"B) : 0x"; - r+=util.hexstrdump(this.MPI); - return r+'\n'; - } - - /** - * Converts the mpi to an BigInteger object - * @return {BigInteger} - */ - function getBigInteger() { - return new BigInteger(util.hexstrdump(this.MPI),16); + var raw = bytes.substr(2, bytelen); + this.fromBytes(raw); + + return 2 + bytelen; } - - function getBits(num) { - for (var i = 0; i < 9; i++) - if (num >> i == 0) - return i; + this.fromBytes = function(bytes) { + this.data = new BigInteger(util.hexstrdump(bytes), 16); } - - /** - * Gets the length of the mpi in bytes - * @return {Integer} Mpi byte length - */ - function getByteLength() { - return this.mpiByteLength; + + this.toBytes = function() { + return this.write().substr(2); } - - /** - * Creates an mpi from the specified string - * @param {String} data Data to read the mpi from - * @return {openpgp_type_mpi} - */ - function create(data) { - this.MPI = data; - this.mpiBitLength = (data.length -1) *8 + getBits(data.charCodeAt(0)); - this.mpiByteLength = data.length; - return this; + + this.byteLength = function() { + return this.toBytes().length; } - + /** * Converts the mpi object to a string as specified in RFC4880 3.2 * @return {String} mpi Byte representation */ - function toBin() { - var result = String.fromCharCode((this.mpiBitLength >> 8) & 0xFF); - result += String.fromCharCode(this.mpiBitLength & 0xFF); - result += this.MPI; - return result; + this.write = function() { + return this.data.toMPI(); + } + + this.toBigInteger = function() { + return this.data.clone(); + } + + this.fromBigInteger = function(bn) { + this.data = bn.clone(); + } + + /** + * Generates debug output (pretty print) + * @return {String} String which gives some information about the mpi + */ + this.toString = function() { + var r = " MPI("+this.mpiBitLength+"b/"+this.mpiByteLength+"B) : 0x"; + r+=util.hexstrdump(this.MPI); + return r+'\n'; } - - this.read = read; - this.toBigInteger = getBigInteger; - this.toString = toString; - this.create = create; - this.toBin = toBin; - this.getByteLength = getByteLength; } diff --git a/src/type/openpgp.type.keyid.js b/src/type/openpgp.type.keyid.js index d8c1ac7a..aa7a9833 100644 --- a/src/type/openpgp.type.keyid.js +++ b/src/type/openpgp.type.keyid.js @@ -24,6 +24,10 @@ formed. */ function openpgp_type_keyid() { + var bytes = ''; + + for(var i = 0; i < 8; i++) + bytes += String.fromCharCode(0); /** * Parsing method for a key id * @param {String} input Input to read the key id from diff --git a/test/general/packet.js b/test/general/packet.js index 682d1cbf..eb72c1f0 100644 --- a/test/general/packet.js +++ b/test/general/packet.js @@ -38,6 +38,8 @@ unittests.register("Packet testing", function() { enc.packets.push(literal); enc.encrypt(algo, key); msg.push(enc); + + var msg2 = new openpgp_packetlist(); msg2.read(msg.write()); @@ -82,16 +84,22 @@ unittests.register("Packet testing", function() { 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()]]; + var key = [ + [key.d, key.p, key.q, key.u], + [key.n, key.ee]]; key = key.map(function(k) { - return k.map(function(v) { + return k.map(function(bn) { var mpi = new openpgp_type_mpi(); - mpi.read(v, 0, v.length); + mpi.fromBigInteger(bn); return mpi; }); }); + + var mpi = new openpgp_type_mpi(); + mpi.fromBigInteger(key[0][1].data); + mpi.read(mpi.write()); + var enc = new openpgp_packet_public_key_encrypted_session_key(), msg = new openpgp_packetlist(), msg2 = new openpgp_packetlist(); @@ -113,6 +121,8 @@ unittests.register("Packet testing", function() { msg2[0].symmetric_algorithm == enc.symmetric_algorithm); }]; + tests.reverse(); + var results = []; for(var i in tests) { @@ -121,4 +131,4 @@ unittests.register("Packet testing", function() { return results; -}) +});