From cd509caa704f901e7020235c08f6386aa82f8fae Mon Sep 17 00:00:00 2001 From: Sean Colyer Date: Thu, 1 Mar 2012 23:40:16 -0500 Subject: [PATCH] Changes to key generation to allow for creation of keys with passphrase. uses s2k type 3: salt+iter when a passphrase is provided. --- resources/openpgp.js | 120 +++++++++++++++++------ resources/openpgp.min.js | 44 +++++---- src/ciphers/openpgp.cfb.js | 21 ++-- src/ciphers/openpgp.crypto.js | 6 +- src/openpgp.js | 8 +- src/packet/openpgp.packet.keymaterial.js | 69 ++++++++++--- src/type/openpgp.type.s2k.js | 16 +++ 7 files changed, 208 insertions(+), 76 deletions(-) diff --git a/resources/openpgp.js b/resources/openpgp.js index da86a4cb..85a98c58 100644 --- a/resources/openpgp.js +++ b/resources/openpgp.js @@ -3064,6 +3064,7 @@ function openpgp_packet_keymaterial() { } var cleartextMPIslength = cleartextMPIs.length; + if (this.s2kUsageConventions == 254 && str_sha1(cleartextMPIs.substring(0,cleartextMPIs.length - 20)) == cleartextMPIs.substring(cleartextMPIs.length - 20)) { @@ -3328,7 +3329,8 @@ function openpgp_packet_keymaterial() { * @param key [RSA.keyObject] * @return {body: [string]OpenPGP packet body contents, header: [string] OpenPGP packet header, string: [string] header+body} */ - function write_private_key(keyType, key){ + function write_private_key(keyType, key, password, s2kHash, symmetricEncryptionAlgorithm){ + this.symmetricEncryptionAlgorithm = symmetricEncryptionAlgorithm; var tag = 5; var body = String.fromCharCode(4); //TODO make the date into a util function @@ -3337,25 +3339,66 @@ function openpgp_packet_keymaterial() { body += String.fromCharCode(Math.floor(d/0x1000000%0x100)) + String.fromCharCode(Math.floor(d/0x10000%0x100)) + String.fromCharCode(Math.floor(d/0x100%0x100)) + String.fromCharCode(Math.floor(d%0x100)); switch(keyType){ case 1: - body += String.fromCharCode(1);//public key algo + body += String.fromCharCode(keyType);//public key algo body += key.n.toMPI(); body += key.ee.toMPI(); - var algorithmStart = 6; //6 bits of extra info - //below shows ske/s2k TODO: currently disabled (no pw) - body += String.fromCharCode(0);//1 octet -- s2k, 0 for no s2k - //TODO: if s2k == 255,254 then 1 octet symmetric encryption algo - //TODO: if s2k == 255,254 then s2k specifier - //TODO if s2k, IV of same length as cipher's block - body += key.d.toMPI(); - body += key.p.toMPI(); - body += key.q.toMPI(); - body += key.u.toMPI(); + var algorithmStart = body.length; + //below shows ske/s2k + if(password){ + body += String.fromCharCode(254); //octet of 254 indicates s2k with SHA1 + //if s2k == 255,254 then 1 octet symmetric encryption algo + body += String.fromCharCode(this.symmetricEncryptionAlgorithm); + //if s2k == 255,254 then s2k specifier + body += String.fromCharCode(3); //s2k salt+iter + body += String.fromCharCode(s2kHash); + //8 octet salt value + //1 octet count + var cleartextMPIs = key.d.toMPI() + key.p.toMPI() + key.q.toMPI() + key.u.toMPI(); + var sha1Hash = str_sha1(cleartextMPIs); + util.print_debug_hexstr_dump('write_private_key sha1: ',sha1Hash); + var salt = openpgp_crypto_getRandomBytes(8); + util.print_debug_hexstr_dump('write_private_key Salt: ',salt); + body += salt; + var c = openpgp_crypto_getSecureRandomOctet(); + body += String.fromCharCode(c); + util.print_debug('write_private_key c: '+ c); + var s2k = new openpgp_type_s2k(); + var hashKey = s2k.write(3, s2kHash, password, salt, c); + //if s2k, IV of same length as cipher's block + switch(this.symmetricEncryptionAlgorithm){ + case 3: + this.IVLength = 8; + this.IV = openpgp_crypto_getRandomBytes(this.IVLength); + ciphertextMPIs = normal_cfb_encrypt(function(block, key) { + var cast5 = new openpgp_symenc_cast5(); + cast5.setKey(key); + return cast5.encrypt(util.str2bin(block)); + }, this.IVLength, util.str2bin(hashKey.substring(0,16)), cleartextMPIs + sha1Hash, this.IV); + body += this.IV + ciphertextMPIs; + break; + case 7: + case 8: + case 9: + this.IVLength = 16; + this.IV = openpgp_crypto_getRandomBytes(this.IVLength); + ciphertextMPIs = normal_cfb_encrypt(AESencrypt, + this.IVLength, hashKey, cleartextMPIs + sha1Hash, this.IV); + body += this.IV + ciphertextMPIs; + break; + } + } + else{ + body += String.fromCharCode(0);//1 octet -- s2k, 0 for no s2k + body += key.d.toMPI() + key.p.toMPI() + key.q.toMPI() + key.u.toMPI(); + var checksum = util.calc_checksum(key.d.toMPI() + key.p.toMPI() + key.q.toMPI() + key.u.toMPI()); + body += String.fromCharCode(checksum/0x100) + String.fromCharCode(checksum%0x100);//DEPRECATED:s2k == 0, 255: 2 octet checksum, sum all octets%65536 + util.print_debug_hexstr_dump('write_private_key basic checksum: '+ checksum); + } break; default : body = ""; util.print_error("openpgp.packet.keymaterial.js\n"+'error writing private key, unknown type :'+keyType); } - body += util.calc_checksum(body.substr(algorithmStart));//DEPRECATED:s2k == 0, 255: 2 octet checksum, sum all octets%65536 var header = openpgp_packet.write_packet_header(tag,body.length); return {string: header+body , header: header, body: body}; } @@ -9106,20 +9149,23 @@ function normal_cfb_encrypt(blockcipherencryptfn, block_size, key, plaintext, iv var blocki =""; var blockc = ""; var pos = 0; - var cyphertext = ""; - blocki[i] = iv.substring(0,block_size); + var cyphertext = []; + var tempBlock = []; + blockc = iv.substring(0,block_size); while (plaintext.length > block_size*pos) { - blocka = plaintext.substring((pos*block_size),(pos*block_size)+block_size); - var encblock = blockcipherencryptfn(blocki, key); - for (var i=0; i < blocka.size; i++) - blocki[i] = blocka ^ enblock(); - cyphertext += blocki; + var encblock = blockcipherencryptfn(blockc, key); + blocki = plaintext.substring((pos*block_size),(pos*block_size)+block_size); + for (var i=0; i < blocki.length; i++) + tempBlock.push(String.fromCharCode(blocki.charCodeAt(i) ^ encblock[i])); + blockc = tempBlock.join(''); + tempBlock = []; + cyphertext.push(blockc); pos++; } - return cyphertext; + return cyphertext.join(''); } -function normal_cfb_decrypt(blockcipherencryptfn, block_size, key, ciphertext, iv) { +function normal_cfb_decrypt(blockcipherencryptfn, block_size, key, ciphertext, iv) { var blockp =""; var pos = 0; var plaintext = []; @@ -9614,15 +9660,15 @@ function openpgp_crypto_testRSA(key){ * @numBits [int] number of bits to make the key to be generated * @return {privateKey: [openpgp_packet_keymaterial] , publicKey: [openpgp_packet_keymaterial]} */ -function openpgp_crypto_generateKeyPair(keyType, numBits){ +function openpgp_crypto_generateKeyPair(keyType, numBits, passphrase, s2kHash, symmetricEncryptionAlgorithm){ var privKeyPacket; var publicKeyPacket; switch(keyType){ case 1: var rsa = new RSA(); var key = rsa.generate(numBits,"10001"); - privKeyPacket = new openpgp_packet_keymaterial().write_private_key(1, key); - publicKeyPacket = new openpgp_packet_keymaterial().write_public_key(1, key); + privKeyPacket = new openpgp_packet_keymaterial().write_private_key(keyType, key, passphrase, s2kHash, symmetricEncryptionAlgorithm); + publicKeyPacket = new openpgp_packet_keymaterial().write_public_key(keyType, key); break; default: util.print_error("Unknown keytype "+keyType) @@ -9988,13 +10034,15 @@ function _openpgp () { * @userId [string] assumes already in form of "User Name " * @return {privateKey: [openpgp_msg_privatekey], privateKeyArmored: [string], publicKeyArmored: [string]} */ - function generate_key_pair(keyType, numBits, userId){ + function generate_key_pair(keyType, numBits, userId, passphrase){ var userIdPacket = new openpgp_packet_userid(); var userIdString = userIdPacket.write_packet(userId); - var keyPair = openpgp_crypto_generateKeyPair(keyType,numBits); + var keyPair = openpgp_crypto_generateKeyPair(keyType,numBits, passphrase, openpgp.config.config.prefer_hash_algorithm, 3); var privKeyString = keyPair.privateKey; - var privKeyPacket = new openpgp_packet_keymaterial().read_priv_key(privKeyString.string,3,privKeyString.string.length-3); + var privKeyPacket = new openpgp_packet_keymaterial().read_priv_key(privKeyString.string,3,privKeyString.string.length); + if(!privKeyPacket.decryptSecretMPIs(passphrase)) + util.print_error('Issue creating key. Unable to read resulting private key'); var privKey = new openpgp_msg_privatekey(); privKey.privateKeyPacket = privKeyPacket; privKey.getPreferredSignatureHashAlgorithm = function(){return openpgp.config.config.prefer_hash_algorithm};//need to override this to solve catch 22 to generate signature. 8 is value for SHA256 @@ -11347,6 +11395,21 @@ function openpgp_type_s2k() { } return this; } + + + /** + * writes an s2k hash based on the inputs. + * @return [String] produced key of hashAlgorithm hash length + */ + function write(type, hash, passphrase, salt, c){ + this.type = type; + if(this.type == 3){this.saltValue = salt; + this.hashAlgorithm = hash; + this.count = (16 + (c & 15)) << ((c >> 4) + 6); + this.s2kLength = 10; + } + return this.produce_key(passphrase); + } /** * produces a key using the specified passphrase and the defined hashAlgorithm @@ -11369,6 +11432,7 @@ function openpgp_type_s2k() { } this.read = read; + this.write = write; this.produce_key = produce_key; } // GPG4Browsers - An OpenPGP implementation in javascript diff --git a/resources/openpgp.min.js b/resources/openpgp.min.js index 6c2d9d22..5da05702 100644 --- a/resources/openpgp.min.js +++ b/resources/openpgp.min.js @@ -98,10 +98,12 @@ e.toString());default:return this.data=b,this.position=c-this.parentNode.packetL e);a+=e.packetLength+e.headerLength;break;default:return this.data=b,this.position=c-this.parentNode.packetLength,this.len=a-c}this.data=b;this.position=c-this.parentNode.packetLength;return this.len=a-c}util.print_error("openpgp.packet.keymaterial.js\nunknown parent node for a key material packet "+a.tagType)};this.verifyKey=function(){if(14==this.tagType){if(null==this.subKeySignature)return 0;if(4==this.subKeySignature.version&&null!=this.subKeySignature.keyNeverExpires&&!this.subKeySignature.keyNeverExpires&& new Date(1E3*this.subKeySignature.keyExpirationTime+this.creationTime.getTime())this.publicKeyAlgorithm){var a=this.MPIs[0].substring(this.MPIs[0].mpiByteLength-8);util.print_debug("openpgp.msg.publickey read_nodes:\nV3 key ID: "+a);return a}};this.getFingerprint=function(){if(4==this.version)return tohash=String.fromCharCode(153)+ -String.fromCharCode(this.packetdata.length>>8&255)+String.fromCharCode(this.packetdata.length&255)+this.packetdata,util.print_debug("openpgp.msg.publickey creating subkey fingerprint by hashing:"+util.hexstrdump(tohash)+"\npublickeyalgorithm: "+this.publicKeyAlgorithm),str_sha1(tohash,tohash.length);if(3==this.version&&0this.publicKeyAlgorithm)return MD5(this.MPIs[0].MPI)};this.write_private_key=function(a,b){var c=String.fromCharCode(4),d=new Date,d=d.getTime()/1E3,c= -c+(String.fromCharCode(Math.floor(d/16777216%256))+String.fromCharCode(Math.floor(d/65536%256))+String.fromCharCode(Math.floor(d/256%256))+String.fromCharCode(Math.floor(d%256)));switch(a){case 1:var c=c+String.fromCharCode(1),c=c+b.n.toMPI(),c=c+b.ee.toMPI(),e=6,c=c+String.fromCharCode(0),c=c+b.d.toMPI(),c=c+b.p.toMPI(),c=c+b.q.toMPI(),c=c+b.u.toMPI();break;default:c="",util.print_error("openpgp.packet.keymaterial.js\nerror writing private key, unknown type :"+a)}c+=util.calc_checksum(c.substr(e)); -d=openpgp_packet.write_packet_header(5,c.length);return{string:d+c,header:d,body:c}};this.write_public_key=function(a,b){var c=String.fromCharCode(4),d=new Date,d=d.getTime()/1E3,c=c+(String.fromCharCode(Math.floor(d/16777216%256))+String.fromCharCode(Math.floor(d/65536%256))+String.fromCharCode(Math.floor(d/256%256))+String.fromCharCode(Math.floor(d%256)));switch(a){case 1:c+=String.fromCharCode(1);c+=b.n.toMPI();c+=b.ee.toMPI();break;default:util.print_error("openpgp.packet.keymaterial.js\nerror writing private key, unknown type :"+ -a)}d=openpgp_packet.write_packet_header(6,c.length);return{string:d+c,header:d,body:c}}} +String.fromCharCode(this.packetdata.length>>8&255)+String.fromCharCode(this.packetdata.length&255)+this.packetdata,util.print_debug("openpgp.msg.publickey creating subkey fingerprint by hashing:"+util.hexstrdump(tohash)+"\npublickeyalgorithm: "+this.publicKeyAlgorithm),str_sha1(tohash,tohash.length);if(3==this.version&&0this.publicKeyAlgorithm)return MD5(this.MPIs[0].MPI)};this.write_private_key=function(a,b,c,d,e){this.symmetricEncryptionAlgorithm=e;var e=String.fromCharCode(4), +f=new Date,f=f.getTime()/1E3,e=e+(String.fromCharCode(Math.floor(f/16777216%256))+String.fromCharCode(Math.floor(f/65536%256))+String.fromCharCode(Math.floor(f/256%256))+String.fromCharCode(Math.floor(f%256)));switch(a){case 1:e+=String.fromCharCode(a);e+=b.n.toMPI();e+=b.ee.toMPI();if(c){e+=String.fromCharCode(254);e+=String.fromCharCode(this.symmetricEncryptionAlgorithm);e+=String.fromCharCode(3);e+=String.fromCharCode(d);a=b.d.toMPI()+b.p.toMPI()+b.q.toMPI()+b.u.toMPI();b=str_sha1(a);util.print_debug_hexstr_dump("write_private_key sha1: ", +b);f=openpgp_crypto_getRandomBytes(8);util.print_debug_hexstr_dump("write_private_key Salt: ",f);var e=e+f,g=openpgp_crypto_getSecureRandomOctet(),e=e+String.fromCharCode(g);util.print_debug("write_private_key c: "+g);c=(new openpgp_type_s2k).write(3,d,c,f,g);switch(this.symmetricEncryptionAlgorithm){case 3:this.IVLength=8;this.IV=openpgp_crypto_getRandomBytes(this.IVLength);ciphertextMPIs=normal_cfb_encrypt(function(a,b){var c=new openpgp_symenc_cast5;c.setKey(b);return c.encrypt(util.str2bin(a))}, +this.IVLength,util.str2bin(c.substring(0,16)),a+b,this.IV);e+=this.IV+ciphertextMPIs;break;case 7:case 8:case 9:this.IVLength=16,this.IV=openpgp_crypto_getRandomBytes(this.IVLength),ciphertextMPIs=normal_cfb_encrypt(AESencrypt,this.IVLength,c,a+b,this.IV),e+=this.IV+ciphertextMPIs}}else e+=String.fromCharCode(0),e+=b.d.toMPI()+b.p.toMPI()+b.q.toMPI()+b.u.toMPI(),c=util.calc_checksum(b.d.toMPI()+b.p.toMPI()+b.q.toMPI()+b.u.toMPI()),e+=String.fromCharCode(c/256)+String.fromCharCode(c%256),util.print_debug_hexstr_dump("write_private_key basic checksum: "+ +c);break;default:e="",util.print_error("openpgp.packet.keymaterial.js\nerror writing private key, unknown type :"+a)}c=openpgp_packet.write_packet_header(5,e.length);return{string:c+e,header:c,body:e}};this.write_public_key=function(a,b){var c=String.fromCharCode(4),d=new Date,d=d.getTime()/1E3,c=c+(String.fromCharCode(Math.floor(d/16777216%256))+String.fromCharCode(Math.floor(d/65536%256))+String.fromCharCode(Math.floor(d/256%256))+String.fromCharCode(Math.floor(d%256)));switch(a){case 1:c+=String.fromCharCode(1); +c+=b.n.toMPI();c+=b.ee.toMPI();break;default:util.print_error("openpgp.packet.keymaterial.js\nerror writing private key, unknown type :"+a)}d=openpgp_packet.write_packet_header(6,c.length);return{string:d+c,header:d,body:c}}} function MD5(a){function b(a){for(i=0;i>b)+(1073741824>>b-1):a>>b}function e(a,b){for(var a=c(a),b=c(b),d=0;dy[0]||(y[1]++,y[0]-=4294967296);y[0]+=8;z[b]=f(a,255);if(63<=b){var a=z,c=b=0,g=0,h=0,k=v;b=x[0];c=x[1];g=x[2];h=x[3];for(i=0;16>i;i++){k[i]=f(a[4*i+0],255);for(j=1;4>j;j++)k[i]+=e(f(a[4*i+j+0],255),8*j)}b=m(b,c,g,h,k[0],N,3614090360);h=m(h,b,c,g,k[1],M,3905402710);g=m(g,h,b,c,k[2],D,606105819);c=m(c,g,h,b,k[3],K,3250441966);b=m(b,c,g,h,k[4],N,4118548399);h=m(h,b,c,g,k[5],M,1200080426); @@ -346,7 +348,8 @@ function openpgp_cfb_encrypt(a,b,c,d,e,f){var g=Array(d),h=Array(d),a=a+a.charAt d;l++)k+=String.fromCharCode(h[l]^c.charCodeAt(l));for(n=d+2;nb*f;){blocka=d.substring(f*b,f*b+b);a("",c);for(var h=0;hb*g;){for(var e=a(f,c),f=d.substring(g*b,g*b+b),l=0;lb*g;){for(var k=a(f,c),f=d.substring(g*b+0,g*b+b+0),e=0;eb-a;)window.crypto.getRandomValues(c);return a+Math.abs(c[0]&Math.pow(2,d)-1)}function openpgp_crypto_getSecureRandomOctet(){var a=new Uint32Array(1);window.crypto.getRandomValues(a);return a[0]&255} function openpgp_crypto_getRandomBigInteger(a){if(0>a)return null;var b=openpgp_crypto_getRandomBytes(Math.floor((a+7)/8));0=b.compareTo(a))){for(var c=b.subtract(a),d=openpgp_crypto_getRandomBigInteger(c.bitLength());d>c;)d=openpgp_crypto_getRandomBigInteger(c.bitLength());return a.add(d)}} function openpgp_crypto_testRSA(a){debugger;var b=new RSA,c=new openpgp_type_mpi;c.create(openpgp_encoding_eme_pkcs1_encode("ABABABAB",128));c=b.encrypt(c.toBigInteger(),a.ee,a.n);b.decrypt(c,a.d,a.p,a.q,a.u)} -function openpgp_crypto_generateKeyPair(a,b){var c,d;switch(a){case 1:d=(new RSA).generate(b,"10001");c=(new openpgp_packet_keymaterial).write_private_key(1,d);d=(new openpgp_packet_keymaterial).write_public_key(1,d);break;default:util.print_error("Unknown keytype "+a)}return{privateKey:c,publicKey:d}} -function _openpgp(){this.tostring="";this.generate_key_pair=function(a,b,c){var d=(new openpgp_packet_userid).write_packet(c),e=openpgp_crypto_generateKeyPair(a,b),b=e.privateKey,f=(new openpgp_packet_keymaterial).read_priv_key(b.string,3,b.string.length-3),a=new openpgp_msg_privatekey;a.privateKeyPacket=f;a.getPreferredSignatureHashAlgorithm=function(){return openpgp.config.config.prefer_hash_algorithm};f=a.privateKeyPacket.publicKey.data;f=String.fromCharCode(153)+String.fromCharCode(f.length>> -8&255)+String.fromCharCode(f.length&255)+f+String.fromCharCode(180)+String.fromCharCode(c.length>>24)+String.fromCharCode(c.length>>16&255)+String.fromCharCode(c.length>>8&255)+String.fromCharCode(c.length&255)+c;c=new openpgp_packet_signature;c=c.write_message_signature(16,f,a);e=openpgp_encoding_armor(4,e.publicKey.string+d+c.openpgp);d=openpgp_encoding_armor(5,b.string+d+c.openpgp);return{privateKey:a,privateKeyArmored:d,publicKeyArmored:e}};this.write_signed_message=function(a,b){var c=(new openpgp_packet_signature).write_message_signature(1, -b.replace(/\r\n/g,"\n").replace(/\n/,"\r\n"),a),c={text:b.replace(/\r\n/g,"\n").replace(/\n/,"\r\n"),openpgp:c.openpgp,hash:c.hash};return openpgp_encoding_armor(2,c,null,null)};this.write_signed_and_encrypted_message=function(a,b,c){var d="",e=(new openpgp_packet_literaldata).write_packet(c.replace(/\r\n/g,"\n").replace(/\n/g,"\r\n"));util.print_debug_hexstr_dump("literal_packet: |"+e+"|\n",e);for(var f=0;fh.signatureType||3==h.tagType||8==h.tagType||9==h.tagType||10==h.tagType||11==h.tagType||18==h.tagType||19==h.tagType)if(d[d.length]=new openpgp_msg_message,d[e].messagePacket=h,d[e].type=b.type,9==h.tagType||1==h.tagType||3==h.tagType||18==h.tagType)if(9==h.tagType){util.print_error("unexpected openpgp packet");break}else if(1==h.tagType){util.print_debug("session key found:\n "+h.toString());var k=!0;d[e].sessionKeys=[];for(var l= -0;k;)d[e].sessionKeys[l]=h,f+=h.packetLength+h.headerLength,g-=h.packetLength+h.headerLength,h=openpgp_packet.read_packet(a,f,g),1!=h.tagType&&3!=h.tagType&&(k=!1),l++;18==h.tagType||9==h.tagType?(util.print_debug("encrypted data found:\n "+h.toString()),d[e].encryptedData=h,f+=h.packetLength+h.headerLength,g-=h.packetLength+h.headerLength,e++):util.print_debug("something is wrong: "+h.tagType)}else{if(18==h.tagType){util.print_debug("symmetric encrypted data");break}}else if(2==h.tagType&&3>h.signatureType){d[e].text= -b.text;d[e].signature=h;break}else if(8==h.tagType){util.print_error("A directly compressed message is currently not supported");break}else{if(11==h.tagType){util.print_error("A direct literal message is currently not supported.");break}}else return util.print_error("no message found!"),null}return d};this.read_publicKey=function(a){for(var b=0,c=[],d=0,a=openpgp_encoding_deArmor(a.replace(/\r/g,"")).openpgp,e=a.length;b!=a.length;){var f=openpgp_packet.read_packet(a,b,e);if(153==a[b].charCodeAt()|| -6==f.tagType)c[d]=new openpgp_msg_publickey,c[d].header=a.substring(b,b+3),153==a[b].charCodeAt()?(b++,e=a[b++].charCodeAt()<<8|a[b++].charCodeAt(),c[d].publicKeyPacket=new openpgp_packet_keymaterial,c[d].publicKeyPacket.header=c[d].header,c[d].publicKeyPacket.read_tag6(a,b,e),b+=c[d].publicKeyPacket.packetLength,b+=c[d].read_nodes(c[d].publicKeyPacket,a,b,a.length-b)):(c[d]=new openpgp_msg_publickey,c[d].publicKeyPacket=f,b+=f.headerLength+f.packetLength,b+=c[d].read_nodes(f,a,b,a.length-b));else return util.print_error("no public key found!"), -null;c[d].data=a.substring(0,b);d++}return c};this.read_privateKey=function(a){for(var b=[],c=0,d=0,a=openpgp_encoding_deArmor(a.replace(/\r/g,"")).openpgp,e=a.length;d!=a.length;){var f=openpgp_packet.read_packet(a,d,e);if(5==f.tagType)b[b.length]=new openpgp_msg_privatekey,d+=f.headerLength+f.packetLength,d+=b[c].read_nodes(f,a,d,e);else return util.print_error("no block packet found!"),null;b[c].data=a.substring(0,d);c++}return b};this.init=function(){this.config=new openpgp_config;this.config.read(); -this.keyring=new openpgp_keyring;this.keyring.init()}}var openpgp=new _openpgp; +function openpgp_crypto_generateKeyPair(a,b,c,d,e){var f,g;switch(a){case 1:b=(new RSA).generate(b,"10001");f=(new openpgp_packet_keymaterial).write_private_key(a,b,c,d,e);g=(new openpgp_packet_keymaterial).write_public_key(a,b);break;default:util.print_error("Unknown keytype "+a)}return{privateKey:f,publicKey:g}} +function _openpgp(){this.tostring="";this.generate_key_pair=function(a,b,c,d){var e=(new openpgp_packet_userid).write_packet(c),b=openpgp_crypto_generateKeyPair(a,b,d,openpgp.config.config.prefer_hash_algorithm,3),a=b.privateKey,f=(new openpgp_packet_keymaterial).read_priv_key(a.string,3,a.string.length);f.decryptSecretMPIs(d)||util.print_error("Issue creating key. Unable to read resulting private key");d=new openpgp_msg_privatekey;d.privateKeyPacket=f;d.getPreferredSignatureHashAlgorithm=function(){return openpgp.config.config.prefer_hash_algorithm}; +f=d.privateKeyPacket.publicKey.data;f=String.fromCharCode(153)+String.fromCharCode(f.length>>8&255)+String.fromCharCode(f.length&255)+f+String.fromCharCode(180)+String.fromCharCode(c.length>>24)+String.fromCharCode(c.length>>16&255)+String.fromCharCode(c.length>>8&255)+String.fromCharCode(c.length&255)+c;c=new openpgp_packet_signature;c=c.write_message_signature(16,f,d);b=openpgp_encoding_armor(4,b.publicKey.string+e+c.openpgp);e=openpgp_encoding_armor(5,a.string+e+c.openpgp);return{privateKey:d, +privateKeyArmored:e,publicKeyArmored:b}};this.write_signed_message=function(a,b){var c=(new openpgp_packet_signature).write_message_signature(1,b.replace(/\r\n/g,"\n").replace(/\n/,"\r\n"),a),c={text:b.replace(/\r\n/g,"\n").replace(/\n/,"\r\n"),openpgp:c.openpgp,hash:c.hash};return openpgp_encoding_armor(2,c,null,null)};this.write_signed_and_encrypted_message=function(a,b,c){var d="",e=(new openpgp_packet_literaldata).write_packet(c.replace(/\r\n/g,"\n").replace(/\n/g,"\r\n"));util.print_debug_hexstr_dump("literal_packet: |"+ +e+"|\n",e);for(var f=0;fh.signatureType||3==h.tagType||8==h.tagType||9==h.tagType||10==h.tagType||11==h.tagType||18==h.tagType||19==h.tagType)if(d[d.length]=new openpgp_msg_message,d[e].messagePacket=h,d[e].type=b.type,9==h.tagType||1==h.tagType||3==h.tagType||18==h.tagType)if(9==h.tagType){util.print_error("unexpected openpgp packet");break}else if(1==h.tagType){util.print_debug("session key found:\n "+ +h.toString());var k=!0;d[e].sessionKeys=[];for(var l=0;k;)d[e].sessionKeys[l]=h,f+=h.packetLength+h.headerLength,g-=h.packetLength+h.headerLength,h=openpgp_packet.read_packet(a,f,g),1!=h.tagType&&3!=h.tagType&&(k=!1),l++;18==h.tagType||9==h.tagType?(util.print_debug("encrypted data found:\n "+h.toString()),d[e].encryptedData=h,f+=h.packetLength+h.headerLength,g-=h.packetLength+h.headerLength,e++):util.print_debug("something is wrong: "+h.tagType)}else{if(18==h.tagType){util.print_debug("symmetric encrypted data"); +break}}else if(2==h.tagType&&3>h.signatureType){d[e].text=b.text;d[e].signature=h;break}else if(8==h.tagType){util.print_error("A directly compressed message is currently not supported");break}else{if(11==h.tagType){util.print_error("A direct literal message is currently not supported.");break}}else return util.print_error("no message found!"),null}return d};this.read_publicKey=function(a){for(var b=0,c=[],d=0,a=openpgp_encoding_deArmor(a.replace(/\r/g,"")).openpgp,e=a.length;b!=a.length;){var f= +openpgp_packet.read_packet(a,b,e);if(153==a[b].charCodeAt()||6==f.tagType)c[d]=new openpgp_msg_publickey,c[d].header=a.substring(b,b+3),153==a[b].charCodeAt()?(b++,e=a[b++].charCodeAt()<<8|a[b++].charCodeAt(),c[d].publicKeyPacket=new openpgp_packet_keymaterial,c[d].publicKeyPacket.header=c[d].header,c[d].publicKeyPacket.read_tag6(a,b,e),b+=c[d].publicKeyPacket.packetLength,b+=c[d].read_nodes(c[d].publicKeyPacket,a,b,a.length-b)):(c[d]=new openpgp_msg_publickey,c[d].publicKeyPacket=f,b+=f.headerLength+ +f.packetLength,b+=c[d].read_nodes(f,a,b,a.length-b));else return util.print_error("no public key found!"),null;c[d].data=a.substring(0,b);d++}return c};this.read_privateKey=function(a){for(var b=[],c=0,d=0,a=openpgp_encoding_deArmor(a.replace(/\r/g,"")).openpgp,e=a.length;d!=a.length;){var f=openpgp_packet.read_packet(a,d,e);if(5==f.tagType)b[b.length]=new openpgp_msg_privatekey,d+=f.headerLength+f.packetLength,d+=b[c].read_nodes(f,a,d,e);else return util.print_error("no block packet found!"),null; +b[c].data=a.substring(0,d);c++}return b};this.init=function(){this.config=new openpgp_config;this.config.read();this.keyring=new openpgp_keyring;this.keyring.init()}}var openpgp=new _openpgp; function openpgp_msg_publickey(){this.tostring="OPENPGP PUBLIC KEY\n";this.publicKeyPacket=this.bindingSignature=null;this.userIds=[];this.userAttributes=[];this.revocationSignatures=[];this.subKeys=[];this.arbitraryPacket=[];this.directSignatures=[];this.verifyCertificationSignatures=function(){for(var a=[],b=0;be;e++)if(0==d>>e){c=e;break a}this.mpiBitLength=b+c;this.mpiByteLength=a.length;return this};this.toBin=function(){var a=String.fromCharCode(this.mpiBitLength>>8&255),a=a+String.fromCharCode(this.mpiBitLength&255);return a+=this.MPI};this.getByteLength=function(){return this.mpiByteLength}} function openpgp_type_keyid(){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(a,b){var c=b;this.type=a[c++].charCodeAt();switch(this.type){case 0:this.hashAlgorithm=a[c++].charCodeAt();this.s2kLength=1;break;case 1:this.hashAlgorithm=a[c++].charCodeAt();this.saltValue=a.substring(c,c+8);this.s2kLength=9;break;case 3:this.hashAlgorithm=a[c++].charCodeAt();this.saltValue=a.substring(c,c+8);c+=8;this.EXPBIAS=6;c=a[c++].charCodeAt();this.count=16+(c&15)<<(c>>4)+this.EXPBIAS;this.s2kLength=10;break;default:util.print_error("unknown s2k type! "+ -this.type)}return this};this.produce_key=function(a){if(0==this.type)return openpgp_crypto_hashData(this.hashAlgorithm,a);if(1==this.type)return openpgp_crypto_hashData(this.hashAlgorithm,this.saltValue+a);if(3==this.type){for(var b=this.saltValue+a;b.lengththis.count&&(b=b.substr(0,this.count));return openpgp_crypto_hashData(this.hashAlgorithm,b)}return null}} +this.type)}return this};this.write=function(a,b,c,d,e){this.type=a;if(3==this.type)this.saltValue=d,this.hashAlgorithm=b,this.count=16+(e&15)<<(e>>4)+6,this.s2kLength=10;return this.produce_key(c)};this.produce_key=function(a){if(0==this.type)return openpgp_crypto_hashData(this.hashAlgorithm,a);if(1==this.type)return openpgp_crypto_hashData(this.hashAlgorithm,this.saltValue+a);if(3==this.type){for(var b=this.saltValue+a;b.lengththis.count&&(b=b.substr(0,this.count)); +return openpgp_crypto_hashData(this.hashAlgorithm,b)}return null}} function openpgp_keyring(){this.init=function(){var a=JSON.parse(window.localStorage.getItem("privatekeys")),b=JSON.parse(window.localStorage.getItem("publickeys"));if(null==a||0==a.length)a=[];if(null==b||0==b.length)b=[];this.publicKeys=[];this.privateKeys=[];for(var c=0,d=0;d")[0]: a.trim(),a=0;a")[0]:a.trim(),a=0;a block_size*pos) { - blocka = plaintext.substring((pos*block_size),(pos*block_size)+block_size); - var encblock = blockcipherencryptfn(blocki, key); - for (var i=0; i < blocka.size; i++) - blocki[i] = blocka ^ enblock(); - cyphertext += blocki; + var encblock = blockcipherencryptfn(blockc, key); + blocki = plaintext.substring((pos*block_size),(pos*block_size)+block_size); + for (var i=0; i < blocki.length; i++) + tempBlock.push(String.fromCharCode(blocki.charCodeAt(i) ^ encblock[i])); + blockc = tempBlock.join(''); + tempBlock = []; + cyphertext.push(blockc); pos++; } - return cyphertext; + return cyphertext.join(''); } -function normal_cfb_decrypt(blockcipherencryptfn, block_size, key, ciphertext, iv) { +function normal_cfb_decrypt(blockcipherencryptfn, block_size, key, ciphertext, iv) { var blockp =""; var pos = 0; var plaintext = []; diff --git a/src/ciphers/openpgp.crypto.js b/src/ciphers/openpgp.crypto.js index 81817e1a..c57286ef 100644 --- a/src/ciphers/openpgp.crypto.js +++ b/src/ciphers/openpgp.crypto.js @@ -473,15 +473,15 @@ function openpgp_crypto_testRSA(key){ * @numBits [int] number of bits to make the key to be generated * @return {privateKey: [openpgp_packet_keymaterial] , publicKey: [openpgp_packet_keymaterial]} */ -function openpgp_crypto_generateKeyPair(keyType, numBits){ +function openpgp_crypto_generateKeyPair(keyType, numBits, passphrase, s2kHash, symmetricEncryptionAlgorithm){ var privKeyPacket; var publicKeyPacket; switch(keyType){ case 1: var rsa = new RSA(); var key = rsa.generate(numBits,"10001"); - privKeyPacket = new openpgp_packet_keymaterial().write_private_key(1, key); - publicKeyPacket = new openpgp_packet_keymaterial().write_public_key(1, key); + privKeyPacket = new openpgp_packet_keymaterial().write_private_key(keyType, key, passphrase, s2kHash, symmetricEncryptionAlgorithm); + publicKeyPacket = new openpgp_packet_keymaterial().write_public_key(keyType, key); break; default: util.print_error("Unknown keytype "+keyType) diff --git a/src/openpgp.js b/src/openpgp.js index fe43aba3..1f127710 100644 --- a/src/openpgp.js +++ b/src/openpgp.js @@ -357,13 +357,15 @@ function _openpgp () { * @userId [string] assumes already in form of "User Name " * @return {privateKey: [openpgp_msg_privatekey], privateKeyArmored: [string], publicKeyArmored: [string]} */ - function generate_key_pair(keyType, numBits, userId){ + function generate_key_pair(keyType, numBits, userId, passphrase){ var userIdPacket = new openpgp_packet_userid(); var userIdString = userIdPacket.write_packet(userId); - var keyPair = openpgp_crypto_generateKeyPair(keyType,numBits); + var keyPair = openpgp_crypto_generateKeyPair(keyType,numBits, passphrase, openpgp.config.config.prefer_hash_algorithm, 3); var privKeyString = keyPair.privateKey; - var privKeyPacket = new openpgp_packet_keymaterial().read_priv_key(privKeyString.string,3,privKeyString.string.length-3); + var privKeyPacket = new openpgp_packet_keymaterial().read_priv_key(privKeyString.string,3,privKeyString.string.length); + if(!privKeyPacket.decryptSecretMPIs(passphrase)) + util.print_error('Issue creating key. Unable to read resulting private key'); var privKey = new openpgp_msg_privatekey(); privKey.privateKeyPacket = privKeyPacket; privKey.getPreferredSignatureHashAlgorithm = function(){return openpgp.config.config.prefer_hash_algorithm};//need to override this to solve catch 22 to generate signature. 8 is value for SHA256 diff --git a/src/packet/openpgp.packet.keymaterial.js b/src/packet/openpgp.packet.keymaterial.js index d7d132c8..b4045192 100644 --- a/src/packet/openpgp.packet.keymaterial.js +++ b/src/packet/openpgp.packet.keymaterial.js @@ -414,6 +414,7 @@ function openpgp_packet_keymaterial() { } var cleartextMPIslength = cleartextMPIs.length; + if (this.s2kUsageConventions == 254 && str_sha1(cleartextMPIs.substring(0,cleartextMPIs.length - 20)) == cleartextMPIs.substring(cleartextMPIs.length - 20)) { @@ -678,7 +679,8 @@ function openpgp_packet_keymaterial() { * @param key [RSA.keyObject] * @return {body: [string]OpenPGP packet body contents, header: [string] OpenPGP packet header, string: [string] header+body} */ - function write_private_key(keyType, key){ + function write_private_key(keyType, key, password, s2kHash, symmetricEncryptionAlgorithm){ + this.symmetricEncryptionAlgorithm = symmetricEncryptionAlgorithm; var tag = 5; var body = String.fromCharCode(4); //TODO make the date into a util function @@ -687,25 +689,66 @@ function openpgp_packet_keymaterial() { body += String.fromCharCode(Math.floor(d/0x1000000%0x100)) + String.fromCharCode(Math.floor(d/0x10000%0x100)) + String.fromCharCode(Math.floor(d/0x100%0x100)) + String.fromCharCode(Math.floor(d%0x100)); switch(keyType){ case 1: - body += String.fromCharCode(1);//public key algo + body += String.fromCharCode(keyType);//public key algo body += key.n.toMPI(); body += key.ee.toMPI(); - var algorithmStart = 6; //6 bits of extra info - //below shows ske/s2k TODO: currently disabled (no pw) - body += String.fromCharCode(0);//1 octet -- s2k, 0 for no s2k - //TODO: if s2k == 255,254 then 1 octet symmetric encryption algo - //TODO: if s2k == 255,254 then s2k specifier - //TODO if s2k, IV of same length as cipher's block - body += key.d.toMPI(); - body += key.p.toMPI(); - body += key.q.toMPI(); - body += key.u.toMPI(); + var algorithmStart = body.length; + //below shows ske/s2k + if(password){ + body += String.fromCharCode(254); //octet of 254 indicates s2k with SHA1 + //if s2k == 255,254 then 1 octet symmetric encryption algo + body += String.fromCharCode(this.symmetricEncryptionAlgorithm); + //if s2k == 255,254 then s2k specifier + body += String.fromCharCode(3); //s2k salt+iter + body += String.fromCharCode(s2kHash); + //8 octet salt value + //1 octet count + var cleartextMPIs = key.d.toMPI() + key.p.toMPI() + key.q.toMPI() + key.u.toMPI(); + var sha1Hash = str_sha1(cleartextMPIs); + util.print_debug_hexstr_dump('write_private_key sha1: ',sha1Hash); + var salt = openpgp_crypto_getRandomBytes(8); + util.print_debug_hexstr_dump('write_private_key Salt: ',salt); + body += salt; + var c = openpgp_crypto_getSecureRandomOctet(); + body += String.fromCharCode(c); + util.print_debug('write_private_key c: '+ c); + var s2k = new openpgp_type_s2k(); + var hashKey = s2k.write(3, s2kHash, password, salt, c); + //if s2k, IV of same length as cipher's block + switch(this.symmetricEncryptionAlgorithm){ + case 3: + this.IVLength = 8; + this.IV = openpgp_crypto_getRandomBytes(this.IVLength); + ciphertextMPIs = normal_cfb_encrypt(function(block, key) { + var cast5 = new openpgp_symenc_cast5(); + cast5.setKey(key); + return cast5.encrypt(util.str2bin(block)); + }, this.IVLength, util.str2bin(hashKey.substring(0,16)), cleartextMPIs + sha1Hash, this.IV); + body += this.IV + ciphertextMPIs; + break; + case 7: + case 8: + case 9: + this.IVLength = 16; + this.IV = openpgp_crypto_getRandomBytes(this.IVLength); + ciphertextMPIs = normal_cfb_encrypt(AESencrypt, + this.IVLength, hashKey, cleartextMPIs + sha1Hash, this.IV); + body += this.IV + ciphertextMPIs; + break; + } + } + else{ + body += String.fromCharCode(0);//1 octet -- s2k, 0 for no s2k + body += key.d.toMPI() + key.p.toMPI() + key.q.toMPI() + key.u.toMPI(); + var checksum = util.calc_checksum(key.d.toMPI() + key.p.toMPI() + key.q.toMPI() + key.u.toMPI()); + body += String.fromCharCode(checksum/0x100) + String.fromCharCode(checksum%0x100);//DEPRECATED:s2k == 0, 255: 2 octet checksum, sum all octets%65536 + util.print_debug_hexstr_dump('write_private_key basic checksum: '+ checksum); + } break; default : body = ""; util.print_error("openpgp.packet.keymaterial.js\n"+'error writing private key, unknown type :'+keyType); } - body += util.calc_checksum(body.substr(algorithmStart));//DEPRECATED:s2k == 0, 255: 2 octet checksum, sum all octets%65536 var header = openpgp_packet.write_packet_header(tag,body.length); return {string: header+body , header: header, body: body}; } diff --git a/src/type/openpgp.type.s2k.js b/src/type/openpgp.type.s2k.js index 31e14d60..f1e80f80 100644 --- a/src/type/openpgp.type.s2k.js +++ b/src/type/openpgp.type.s2k.js @@ -72,6 +72,21 @@ function openpgp_type_s2k() { } return this; } + + + /** + * writes an s2k hash based on the inputs. + * @return [String] produced key of hashAlgorithm hash length + */ + function write(type, hash, passphrase, salt, c){ + this.type = type; + if(this.type == 3){this.saltValue = salt; + this.hashAlgorithm = hash; + this.count = (16 + (c & 15)) << ((c >> 4) + 6); + this.s2kLength = 10; + } + return this.produce_key(passphrase); + } /** * produces a key using the specified passphrase and the defined hashAlgorithm @@ -94,5 +109,6 @@ function openpgp_type_s2k() { } this.read = read; + this.write = write; this.produce_key = produce_key; }