From 1e49e8ee23f08f1387f3b823ae579608dad5a12b Mon Sep 17 00:00:00 2001 From: Michal Kolodziej Date: Thu, 9 May 2013 14:25:09 +0200 Subject: [PATCH] Encryption of secret key packets is working. --- resources/openpgp.js | 370 ++++++------------ resources/openpgp.min.js | 45 +-- src/encoding/openpgp.encoding.asciiarmor.js | 2 +- src/packet/compressed.js | 11 - src/packet/packet.js | 3 +- src/packet/packetlist.js | 3 + src/packet/public_key.js | 2 +- .../public_key_encrypted_session_key.js | 25 -- src/packet/secret_key.js | 283 ++++++-------- .../sym_encrypted_integrity_protected.js | 15 - src/packet/sym_encrypted_session_key.js | 15 - src/packet/symmetrically_encrypted.js | 9 +- src/type/s2k.js | 2 +- test/general/packet.js | 54 ++- 14 files changed, 323 insertions(+), 516 deletions(-) diff --git a/resources/openpgp.js b/resources/openpgp.js index af77ac3a..a060ca84 100644 --- a/resources/openpgp.js +++ b/resources/openpgp.js @@ -7582,7 +7582,7 @@ function r2s(t) { * or an object with attribute "text" containing the message text * and an attribute "openpgp" containing the bytes. */ -function openpgp_encoding_deArmor(text) { +function openpgp_encoding_dearmor(text) { text = text.replace(/\r/g, '') var type = openpgp_encoding_get_type(text); @@ -9495,17 +9495,6 @@ function openpgp_packet_compressed() { break; } } - - - /** - * Pretty printing the packet (useful for debug purposes) - * @return {String} - */ - this.toString = function() { - return '5.6. Compressed Data Packet (Tag 8)\n'+ - ' Compression Algorithm = '+this.algorithm+'\n'+ - ' Compressed Data: Byte ['+util.hexstrdump(this.compressed)+']\n'; - } }; // GPG4Browsers - An OpenPGP implementation in javascript // Copyright (C) 2011 Recurity Labs GmbH @@ -9924,7 +9913,7 @@ function _openpgp_packet() { * @param {integer} len Length of the input from position on * @return {Object} Returns a parsed openpgp_packet */ - function read_packet(input, position, len) { + this.read_packet = function(input, position, len) { // some sanity checks if (input == null || input.length <= position || input.substring(position).length < 2 @@ -10103,7 +10092,6 @@ function _openpgp_packet() { }; } - this.read_packet = read_packet; /** @@ -10155,6 +10143,9 @@ var openpgp_packet = new _openpgp_packet(); * are stored as numerical indices. */ function openpgp_packetlist() { + /** The number of packets contained within the list. + * @readonly + * @type {Integer} */ this.length = 0; @@ -10374,31 +10365,6 @@ function openpgp_packet_public_key_encrypted_session_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 @@ -10430,7 +10396,7 @@ function openpgp_packet_public_key_encrypted_session_key() { function openpgp_packet_public_key() { this.tag = 6; this.version = 4; - this.created = null; + this.created = new Date(); this.mpi = []; this.algorithm = openpgp.publickey.rsa_sign; @@ -10612,11 +10578,7 @@ function openpgp_packet_secret_key() { this.tag = 5; this.public_key = new openpgp_packet_public_key(); this.mpi = []; - this.symmetric_algorithm = openpgp.symmetric.plaintext; - this.hash_algorithm = openpgp.hash.sha1; - this.s2k = null; this.encrypted = null; - this.iv = null; function get_hash_len(hash) { @@ -10646,7 +10608,7 @@ function openpgp_packet_secret_key() { var hash = hashfn(cleartext); if(hash != hashtext) - throw "Hash mismatch!"; + throw new Error("Hash mismatch."); var mpis = openpgp_crypto_getPrivateMpiCount(algorithm); @@ -10660,6 +10622,19 @@ function openpgp_packet_secret_key() { return mpi; } + function write_cleartext_mpi(hash_algorithm, mpi) { + var bytes= ''; + for(var i in mpi) { + bytes += mpi[i].write(); + } + + + bytes += get_hash_fn(hash_algorithm)(bytes); + + return bytes; + } + + // 5.5.3. Secret-Key Packet Formats /** @@ -10680,60 +10655,20 @@ function openpgp_packet_secret_key() { // indicates that the secret-key data is not encrypted. 255 or 254 // indicates that a string-to-key specifier is being given. Any // other value is a symmetric-key encryption algorithm identifier. - var s2k_usage = bytes[0].charCodeAt(); - - var i = 1; + var isEncrypted = bytes[0].charCodeAt(); - // - [Optional] If string-to-key usage octet was 255 or 254, a one- - // octet symmetric encryption algorithm. - if (s2k_usage == 255 || s2k_usage == 254) { - this.symmetric_algorithm = bytes[i++].charCodeAt(); - - // - [Optional] If string-to-key usage octet was 255 or 254, a - // string-to-key specifier. The length of the string-to-key - // specifier is implied by its type, as described above. - this.s2k = new openpgp_type_s2k(); - i += this.s2k.read(bytes.substr(i)); - } - - // - [Optional] If secret data is encrypted (string-to-key usage octet - // not zero), an Initial Vector (IV) of the same length as the - // cipher's block size. + if(isEncrypted) { + this.encrypted = bytes; + } else { + + // - Plain or encrypted multiprecision integers comprising the secret + // key data. These algorithm-specific fields are as described + // below. - if (s2k_usage != 0 && s2k_usage != 255 && - s2k_usage != 254) { - this.symmetric_algorithm = s2k_usage; - } - - if (s2k_usage != 0 && this.s2k.type != 1001) { - this.iv = bytes.substr(i, - openpgp_crypto_getBlockLength(this.symmetric_algorithm)); - - i += this.iv.length; - } - - if(s2k_usage == 254) - this.hash_algorithm = openpgp.hash.sha1; - else - this.hash_algorithm = 'checksum'; - - // - Plain or encrypted multiprecision integers comprising the secret - // key data. These algorithm-specific fields are as described - // below. - - // s2k type 1001 corresponds to GPG specific extension without primary key secrets - // http://www.gnupg.org/faq/GnuPG-FAQ.html#how-can-i-use-gnupg-in-an-automated-environment - if (s2k_usage != 0 && this.s2k.type == 1001) { - this.mpi = null; - this.encrypted = null; - - } else if (s2k_usage != 0) { - this.encrypted = bytes.substr(i); - - } else { - this.mpi = parse_cleartext_mpi(this.hash_algorithm, bytes.substr(i), + this.mpi = parse_cleartext_mpi('mod', bytes.substr(1), this.public_key.algorithm); - } + } + } /* @@ -10742,7 +10677,7 @@ function openpgp_packet_secret_key() { * @param {Integer} keyType Follows the OpenPGP algorithm standard, * IE 1 corresponds to RSA. * @param {RSA.keyObject} key - * @param password + * @param passphrase * @param s2kHash * @param symmetricEncryptionAlgorithm * @param timePacket @@ -10752,25 +10687,11 @@ function openpgp_packet_secret_key() { this.write = function() { var bytes = this.public_key.write(); - if(this.encrypted == null) { + if(!this.encrypted) { bytes += String.fromCharCode(0); - var mpi = ''; - for(var i in this.mpi) { - mpi += this.mpi[i].write(); - } - - bytes += mpi; - - // TODO check the cheksum! - bytes += openpgp_packet_number_write(util.calc_checksum(mpi), 2); - } else if(this.s2k == null) { - bytes += String.fromCharCode(this.symmetric_algorithm); - bytes += this.encrypted; + bytes += write_cleartext_mpi('mod', this.mpi); } else { - bytes += String.fromCharCode(254); - bytes += String.fromCharCode(this.symmetric_algorithm); - bytes += this.s2k.write(); bytes += this.encrypted; } @@ -10780,75 +10701,54 @@ function openpgp_packet_secret_key() { - this.encrypt = function(password) { + /** Encrypt the payload. By default, we use aes256 and iterated, salted string + * to key specifier + * @param {String} passphrase + */ + this.encrypt = function(passphrase) { + + var s2k = new openpgp_type_s2k(), + symmetric = openpgp.symmetric.aes256, + cleartext = write_cleartext_mpi(openpgp.hash.sha1, this.mpi), + key = produceEncryptionKey(s2k, passphrase, symmetric), + blockLen = openpgp_crypto_getBlockLength(symmetric), + iv = openpgp_crypto_getRandomBytes(blockLen); - switch(keyType){ - case 1: - body += String.fromCharCode(keyType);//public key algo - body += key.n.toMPI(); - body += key.ee.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.symmetric_algorithm); - //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 cleartext = key.d.toMPI() + key.p.toMPI() + key.q.toMPI() + key.u.toMPI(); - var sha1Hash = str_sha1(cleartext); - 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 = 96; //c of 96 translates to count of 65536 - 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.symmetric_algorithm){ - case 3: - this.iv.length = 8; - this.iv = openpgp_crypto_getRandomBytes(this.iv.length); - ciphertextMPIs = normal_cfb_encrypt(function(block, key) { - var cast5 = new openpgp_symenc_cast5(); - cast5.setKey(key); - return cast5.encrypt(util.str2bin(block)); - }, this.iv.length, util.str2bin(hashKey.substring(0,16)), cleartext + sha1Hash, this.iv); - body += this.iv + ciphertextMPIs; - break; - case 7: - case 8: - case 9: - this.iv.length = 16; - this.iv = openpgp_crypto_getRandomBytes(this.iv.length); - ciphertextMPIs = normal_cfb_encrypt(AESencrypt, - this.iv.length, hashKey, cleartext + 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); - } - var header = openpgp_packet.write_packet_header(tag,body.length); - return {string: header+body , header: header, body: body}; + this.encrypted = ''; + this.encrypted += String.fromCharCode(254); + this.encrypted += String.fromCharCode(symmetric); + this.encrypted += s2k.write(); + this.encrypted += iv; + + console.log(cleartext); + + switch(symmetric) { + case 3: + this.encrypted += normal_cfb_encrypt(function(block, key) { + var cast5 = new openpgp_symenc_cast5(); + cast5.setKey(key); + return cast5.encrypt(util.str2bin(block)); + }, iv.length, key, cleartext, iv); + break; + case 7: + case 8: + case 9: + var fn = function(block,key) { + return AESencrypt(util.str2bin(block),key); + } + this.encrypted += normal_cfb_encrypt(fn, + iv.length, new keyExpansion(key), cleartext, iv); + break; + default: + throw new Error("Unsupported symmetric encryption algorithm."); + } } + function produceEncryptionKey(s2k, passphrase, algorithm) { + return s2k.produce_key(passphrase, + openpgp_crypto_getKeyLength(algorithm)); + } /** * Decrypts the private key MPIs which are needed to use the key. @@ -10861,38 +10761,65 @@ function openpgp_packet_secret_key() { * @return {Boolean} True if the passphrase was correct; false if not */ this.decrypt = function(passphrase) { - if (this.encrypted == null) + if (!this.encrypted) return; - // creating a key out of the passphrase - var key = this.s2k.produce_key(passphrase, - openpgp_crypto_getKeyLength(this.symmetric_algorithm)); + var i = 0, + symmetric, + key; - var cleartext = ''; + var s2k_usage = this.encrypted[i++].charCodeAt(); + + // - [Optional] If string-to-key usage octet was 255 or 254, a one- + // octet symmetric encryption algorithm. + if (s2k_usage == 255 || s2k_usage == 254) { + symmetric = this.encrypted[i++].charCodeAt(); + + // - [Optional] If string-to-key usage octet was 255 or 254, a + // string-to-key specifier. The length of the string-to-key + // specifier is implied by its type, as described above. + var s2k = new openpgp_type_s2k(); + i += s2k.read(this.encrypted.substr(i)); + + key = produceEncryptionKey(s2k, passphrase, symmetric); + } else { + symmetric = s2k_usage; + key = MD5(passphrase); + } + + // - [Optional] If secret data is encrypted (string-to-key usage octet + // not zero), an Initial Vector (IV) of the same length as the + // cipher's block size. + var iv = this.encrypted.substr(i, + openpgp_crypto_getBlockLength(symmetric)); + + i += iv.length; + + var cleartext, + ciphertext = this.encrypted.substr(i); - switch (this.symmetric_algorithm) { + switch (symmetric) { case 1: // - IDEA [IDEA] - util.print_error("openpgp.packet.keymaterial.js\n" - +"symmetric encryption algorithim: IDEA is not implemented"); + throw new Error("IDEA is not implemented."); return false; case 2: // - TripleDES (DES-EDE, [SCHNEIER] [HAC] - 168 bit key derived from 192) cleartext = normal_cfb_decrypt(function(block, key) { return des(key, block,1,null,0); - }, this.iv.length, key, this.encrypted, this.iv); + }, iv.length, key, ciphertext, iv); break; case 3: // - CAST5 (128 bit key, as per [RFC2144]) cleartext = normal_cfb_decrypt(function(block, key) { var cast5 = new openpgp_symenc_cast5(); cast5.setKey(key); return cast5.encrypt(util.str2bin(block)); - }, this.iv.length, util.str2bin(key.substring(0,16)), this.encrypted, this.iv); + }, iv.length, util.str2bin(key.substring(0,16)), ciphertext, iv); break; case 4: // - Blowfish (128 bit key, 16 rounds) [BLOWFISH] cleartext = normal_cfb_decrypt(function(block, key) { var blowfish = new Blowfish(key); return blowfish.encrypt(block); - }, this.iv.length, key, this.encrypted, this.iv); + }, iv.length, key, ciphertext, iv); break; case 7: // - AES with 128-bit key [AES] case 8: // - AES with 192-bit key @@ -10900,27 +10827,27 @@ function openpgp_packet_secret_key() { cleartext = normal_cfb_decrypt(function(block,key){ return AESencrypt(util.str2bin(block),key); }, - this.iv.length, keyExpansion(key), - this.encrypted, this.iv); + iv.length, new keyExpansion(key), + ciphertext, iv); break; case 10: // - Twofish with 256-bit key [TWOFISH] - util.print_error("openpgp.packet.keymaterial.js\n"+"Key material is encrypted with twofish: not implemented"); + throw new Error("Twofish is not implemented."); return false; case 5: // - Reserved case 6: // - Reserved default: - util.print_error("openpgp.packet.keymaterial.js\n"+"unknown encryption algorithm for secret key :"+this.symmetric_algorithm); + throw new Error("Unknown symmetric algorithm."); return false; } - - if (cleartext == null) { - util.print_error("openpgp.packet.keymaterial.js\n"+"cleartext was null"); - return false; - } - + + var hash; + if(s2k_usage == 254) + hash = openpgp.hash.sha1; + else + hash = 'mod'; - - this.mpi = parse_cleartext_mpi(this.hash_algorithm, cleartext, + + this.mpi = parse_cleartext_mpi(hash, cleartext, this.public_key.algorithm); } @@ -11674,21 +11601,6 @@ function openpgp_packet_sym_encrypted_integrity_protected() { else this.packets.read(decrypted.substr(0, decrypted.length - 22)); } - - this.toString = function() { - var data = ''; - if(openpgp.config.debug) - data = ' data: Bytes [' - + util.hexstrdump(this.encrypted) + ']'; - - return '5.13. Sym. Encrypted Integrity Protected Data Packet (Tag 18)\n' - + '\n' - + ' version: ' - + this.version - + '\n' - + data; - } - }; // GPG4Browsers - An OpenPGP implementation in javascript // Copyright (C) 2011 Recurity Labs GmbH @@ -11819,21 +11731,6 @@ function openpgp_packet_sym_encrypted_session_key() { openpgp_crypto_getPrefixRandom(this.private_algorithm), this.private_algorithm, key, private_key, true); } - - /** - * 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 @@ -11867,6 +11764,7 @@ function openpgp_packet_sym_encrypted_session_key() { function openpgp_packet_symmetrically_encrypted() { this.tag = 9; this.encrypted = null; + /** Decrypted packets contained within. */ this.packets = new openpgp_packetlist(); @@ -11902,14 +11800,6 @@ function openpgp_packet_symmetrically_encrypted() { this.encrypted = openpgp_crypto_symmetricEncrypt( openpgp_crypto_getPrefixRandom(algo), algo, key, data, true); } - - - this.toString = function () { - return '5.7. Symmetrically Encrypted Data Packet (Tag 9)\n' - + ' Used symmetric algorithm: ' + this.algorithmType + '\n' - + ' encrypted data: Bytes [' - + util.hexstrdump(this.encryptedData) + ']\n'; - } }; @@ -12400,7 +12290,7 @@ function openpgp_type_s2k() { this.algorithm = openpgp.hash.sha256; /** @type {openpgp_type_s2k.type} */ this.type = openpgp_type_s2k.type.iterated; - this.c = 10; + this.c = 96; /** @type {openpgp_bytearray} * Eight bytes of salt. */ this.salt = openpgp_crypto_getRandomBytes(8); diff --git a/resources/openpgp.min.js b/resources/openpgp.min.js index f2ccf999..9caa9f70 100644 --- a/resources/openpgp.min.js +++ b/resources/openpgp.min.js @@ -288,7 +288,7 @@ JXG.Util.genUUID=function(){for(var a="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcde 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.20130509";this.commentstring="http://openpgpjs.org";this.debug=!1;this.read=function(){var a=JSON.parse(window.localStorage.getItem("config"));null==a?(this.config=this.default_config,this.write()):this.config=a};this.write=function(){window.localStorage.setItem("config", JSON.stringify(this.config))}}var b64s="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";function s2r(a){var b,c,d,e="",f=0,h=0,g=a.length;for(d=0;d>2&63),b=(c&3)<<4):1==h?(e+=b64s.charAt(b|c>>4&15),b=(c&15)<<2):2==h&&(e+=b64s.charAt(b|c>>6&3),f+=1,0==f%60&&(e+="\n"),e+=b64s.charAt(c&63)),f+=1,0==f%60&&(e+="\n"),h+=1,3==h&&(h=0);0>6-e&255)),e=e+2&7,f=b<a.length)return util.print_error("openpgp.packet.encryptedsessionkey.js\ninvalid length"),null;this.version=a[0].charCodeAt();this.public_key_id.read_packet(a,1);this.public_key_algorithm=a[9].charCodeAt(); var b=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(a.substr(b));break;case openpgp.publickey.elgamal:this.encrypted=[];this.encrypted[0]=new openpgp_type_mpi;b+=this.encrypted[0].read(a.substr(b));this.encrypted[1]=new openpgp_type_mpi;this.encrypted[1].read(a.substr(b));break;default:util.print_error("openpgp.packet.encryptedsessionkey.js\nunknown public key packet algorithm type "+ this.public_key_algorithm)}};this.write=function(){for(var a=String.fromCharCode(this.version),a=a+this.public_key_id.bytes,a=a+String.fromCharCode(this.public_key_algorithm),b=0;b>8&255),b=b+String.fromCharCode(c&255),c=new openpgp_type_mpi;c.fromBytes(openpgp_encoding_eme_pkcs1_encode(b, -a[0].byteLength()));this.encrypted=openpgp_crypto_asymetricEncrypt(this.public_key_algorithm,a,c)};this.decrypt=function(a,b){var c=openpgp_crypto_asymetricDecrypt(this.public_key_algorithm,a,b,this.encrypted).toBytes(),d=(c.charCodeAt(c.length-2)<<8)+c.charCodeAt(c.length-1),c=openpgp_encoding_eme_pkcs1_decode(c,a[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 a="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",b=0;bthis.algorithm?2:16==this.algorithm?3:17==this.algorithm?4:0;this.mpi=[];for(var a=a.substr(6),c=0,d=0;da.length&&util.print_error("openpgp.packet.keymaterial.js\nerror reading MPI @:"+c);return c+6}util.print_error("Unknown packet version")}};this.write=function(){var a=String.fromCharCode(4),a=a+openpgp_packet_time_write(this.created),a=a+String.fromCharCode(this.algorithm),b;for(b in this.mpi)a+=this.mpi[b].write();return a}}function openpgp_packet_public_subkey(){openpgp_packet_public_key.call(this);this.tag=14} -function openpgp_packet_secret_key(){function a(a){return a==openpgp.hash.sha1?str_sha1:function(a){return openpgp_packet_number_write(util.calc_checksum(a),2)}}function b(b,d,e){var f=b==openpgp.hash.sha1?20:2,b=a(b),h=d.substr(d.length-f),d=d.substr(0,d.length-f);if(b(d)!=h)throw"Hash mismatch!";e=openpgp_crypto_getPrivateMpiCount(e);f=0;b=[];for(h=0;hthis.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)}}function openpgp_packet_secret_subkey(){openpgp_packet_secret_key.call(this);this.tag=7} +a[0].byteLength()));this.encrypted=openpgp_crypto_asymetricEncrypt(this.public_key_algorithm,a,c)};this.decrypt=function(a,b){var c=openpgp_crypto_asymetricDecrypt(this.public_key_algorithm,a,b,this.encrypted).toBytes(),d=(c.charCodeAt(c.length-2)<<8)+c.charCodeAt(c.length-1),c=openpgp_encoding_eme_pkcs1_decode(c,a[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))}} +function openpgp_packet_public_key(){this.tag=6;this.version=4;this.created=new Date;this.mpi=[];this.algorithm=openpgp.publickey.rsa_sign;this.read=function(a){this.version=a[0].charCodeAt();if(3!=this.version){if(4==this.version){this.created=openpgp_packet_time_read(a.substr(1,4));this.algorithm=a[5].charCodeAt();var b=0this.algorithm?2:16==this.algorithm?3:17==this.algorithm?4:0;this.mpi=[];for(var a=a.substr(6),c=0,d=0;da.length&&util.print_error("openpgp.packet.keymaterial.js\nerror reading MPI @:"+c);return c+6}util.print_error("Unknown packet version")}};this.write=function(){var a=String.fromCharCode(4),a=a+openpgp_packet_time_write(this.created),a=a+String.fromCharCode(this.algorithm),b;for(b in this.mpi)a+=this.mpi[b].write();return a}}function openpgp_packet_public_subkey(){openpgp_packet_public_key.call(this);this.tag=14} +function openpgp_packet_secret_key(){function a(a){return a==openpgp.hash.sha1?str_sha1:function(a){return openpgp_packet_number_write(util.calc_checksum(a),2)}}function b(b,c,f){var h=b==openpgp.hash.sha1?20:2,b=a(b),g=c.substr(c.length-h),c=c.substr(0,c.length-h);if(b(c)!=g)throw Error("Hash mismatch.");f=openpgp_crypto_getPrivateMpiCount(f);h=0;b=[];for(g=0;gthis.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)}} +function openpgp_packet_secret_subkey(){openpgp_packet_secret_key.call(this);this.tag=7} function openpgp_packet_signature(){this.tag=2;this.issuerKeyId=this.revocationKeyFingerprint=this.revocationKeyAlgorithm=this.revocationKeyClass=this.preferredSymmetricAlgorithms=this.keyNeverExpires=this.keyExpirationTime=this.revocable=this.regularExpression=this.trustAmount=this.trustLevel=this.exportable=this.hashAlgorithm=this.publicKeyAlgorithm=this.mpi=this.signedHashValue=this.signatureNeverExpires=this.signatureExpirationTime=this.signatureData=this.created=this.signatureType=null;this.notation= {};this.embeddedSignature=this.signatureTargetHash=this.signatureTargetHashAlgorithm=this.signatureTargetPublicKeyAlgorithm=this.reasonForRevocationString=this.reasonForRevocationFlag=this.signersUserId=this.keyFlags=this.policyURI=this.isPrimaryUserID=this.preferredKeyServer=this.keyServerPreferences=this.preferredCompressionAlgorithms=this.preferredHashAlgorithms=null;this.verified=!1;this.read=function(a){var b=0;this.version=a[b++].charCodeAt();switch(this.version){case 3:5!=a[b++].charCodeAt()&& util.print_debug("openpgp.packet.signature.js\ninvalid One-octet length of following hashed material.MUST be 5. @:"+(b-1));this.signatureType=a[b++].charCodeAt();this.created=openpgp_packet_time_read(a.substr(b,4));b+=4;this.signatureData=a.substring(position,b);this.issuerKeyId=a.substring(b,b+8);b+=8;this.publicKeyAlgorithm=a[b++].charCodeAt();this.hashAlgorithm=a[b++].charCodeAt();break;case 4:this.signatureType=a[b++].charCodeAt();this.publicKeyAlgorithm=a[b++].charCodeAt();this.hashAlgorithm= @@ -402,23 +398,20 @@ b){var c=this.toSign(this.signatureType,b),d;d=""+String.fromCharCode(this.versi openpgp_packet_signature.type={binary:0,text:1,standalone:2,cert_generic:16,cert_persona:17,cert_casual:18,cert_positive:19,cert_revocation:48,subkey_binding:24,key_binding:25,key:31,key_revocation:32,subkey_revocation:40,timestamp:64,third_party:80}; function openpgp_packet_sym_encrypted_integrity_protected(){this.tag=18;this.version=1;this.encrypted=null;this.modification=!1;this.packets=new openpgp_packetlist;this.read=function(a){this.version=a[0].charCodeAt();if(1!=this.version)return util.print_error("openpgp.packet.encryptedintegrityprotecteddata.js\nunknown encrypted integrity protected data packet version: "+this.version+"hex:"+util.hexstrdump(a)),null;this.encrypted=a.substr(1)};this.write=function(){return String.fromCharCode(this.version)+ this.encrypted};this.encrypt=function(a,b){var c=this.packets.write(),d=openpgp_crypto_getPrefixRandom(a),e=d+d.charAt(d.length-2)+d.charAt(d.length-1),c=c+String.fromCharCode(211),c=c+String.fromCharCode(20);util.print_debug_hexstr_dump("data to be hashed:",e+c);c+=str_sha1(e+c);util.print_debug_hexstr_dump("hash:",c.substring(c.length-20,c.length));this.encrypted=openpgp_crypto_symmetricEncrypt(d,a,b,c,!1).substring(0,e.length+c.length)};this.decrypt=function(a,b){var c=openpgp_crypto_symmetricDecrypt(a, -b,this.encrypted,!1);this.hash=str_sha1(openpgp_crypto_MDCSystemBytes(a,b,this.encrypted)+c.substring(0,c.length-20));util.print_debug_hexstr_dump("calc hash = ",this.hash);this.hash!=c.substr(c.length-20,20)?(this.packets=new openpgp_packetlist,util.print_error("Decryption stopped: discovered a modification of encrypted data.")):this.packets.read(c.substr(0,c.length-22))};this.toString=function(){var a="";openpgp.config.debug&&(a=" data: Bytes ["+util.hexstrdump(this.encrypted)+"]");return"5.13. Sym. Encrypted Integrity Protected Data Packet (Tag 18)\n\n version: "+ -this.version+"\n"+a}} +b,this.encrypted,!1);this.hash=str_sha1(openpgp_crypto_MDCSystemBytes(a,b,this.encrypted)+c.substring(0,c.length-20));util.print_debug_hexstr_dump("calc hash = ",this.hash);this.hash!=c.substr(c.length-20,20)?(this.packets=new openpgp_packetlist,util.print_error("Decryption stopped: discovered a modification of encrypted data.")):this.packets.read(c.substr(0,c.length-22))}} function openpgp_packet_sym_encrypted_session_key(){this.tag=3;this.private_algorithm=null;this.algorithm=openpgp.symmetric.aes256;this.encrypted=null;this.s2k=new openpgp_type_s2k;this.read=function(a){this.version=a[0].charCodeAt();var b=a[1].charCodeAt(),c=this.s2k.read(a.substr(2))+2;c>8*(b-d-1)&255);return c}function openpgp_packet_time_read(a){var a=openpgp_packet_number_read(a),b=new Date;b.setTime(1E3*a);return b} -function openpgp_packet_time_write(a){a=Math.round(a.getTime()/1E3);return openpgp_packet_number_write(a,4)}function openpgp_packet_user_attribute(){this.tag=17;this.attributes=[];this.read=function(a){for(var b=0;b>8*(b-d-1)&255);return c}function openpgp_packet_time_read(a){var a=openpgp_packet_number_read(a),b=new Date;b.setTime(1E3*a);return b}function openpgp_packet_time_write(a){a=Math.round(a.getTime()/1E3);return openpgp_packet_number_write(a,4)} +function openpgp_packet_user_attribute(){this.tag=17;this.attributes=[];this.read=function(a){for(var b=0;bb;b++)a+=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(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);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;var d=a[c++].charCodeAt();this.count=16+(d&15)<<(d>>4)+this.EXPBIAS;this.s2kLength=10;break;case 101:"GNU"==a.substring(c+ 1,c+4)?(this.hashAlgorithm=a[c++].charCodeAt(),c+=3,d=1E3+a[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-b;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,b){var a=util.encode_utf8(a),c;if(0==this.type)c=openpgp_crypto_hashData(this.hashAlgorithm,a);else if(1==this.type)c=openpgp_crypto_hashData(this.hashAlgorithm,this.saltValue+a);else if(3==this.type){c=[];for(c[0]=this.saltValue+a;c.length*(this.saltValue+a).lengththis.count&&(c=c.substr(0,this.count));c=b&&(24==b||32==b)?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,b)}} -function openpgp_type_s2k(){this.algorithm=openpgp.hash.sha256;this.type=openpgp_type_s2k.type.iterated;this.c=10;this.salt=openpgp_crypto_getRandomBytes(8);this.get_count=function(){return 16+(this.c&15)<<(this.c>>4)+6};this.read=function(a){var b=0;this.type=a[b++].charCodeAt();this.algorithm=a[b++].charCodeAt();var c=openpgp_type_s2k.type;switch(this.type){case c.simple:break;case c.salted:this.salt=a.substr(b,8);b+=8;break;case c.iterated:this.salt=a.substr(b,8);b+=8;this.c=a[b++].charCodeAt(); +function openpgp_type_s2k(){this.algorithm=openpgp.hash.sha256;this.type=openpgp_type_s2k.type.iterated;this.c=96;this.salt=openpgp_crypto_getRandomBytes(8);this.get_count=function(){return 16+(this.c&15)<<(this.c>>4)+6};this.read=function(a){var b=0;this.type=a[b++].charCodeAt();this.algorithm=a[b++].charCodeAt();var c=openpgp_type_s2k.type;switch(this.type){case c.simple:break;case c.salted:this.salt=a.substr(b,8);b+=8;break;case c.iterated:this.salt=a.substr(b,8);b+=8;this.c=a[b++].charCodeAt(); break;case c.gnu:"GNU"==a.substr(b,3)?(b+=3,a=1E3+a[b++].charCodeAt(),1001==a?this.type=a: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 b};this.write=function(){var a=String.fromCharCode(this.type),a=a+String.fromCharCode(this.algorithm),b=openpgp_type_s2k.type;switch(this.type){case b.salted:a+=this.salt;break;case b.iterated:a+=this.salt,a+=String.fromCharCode(this.c)}return a}; this.produce_key=function(a,b){for(var a=util.encode_utf8(a),c="",d="";c.length<=b;){var e;a:{e=d;var f=openpgp_type_s2k.type;switch(this.type){case f.simple:e=openpgp_crypto_hashData(this.algorithm,e+a);break a;case f.salted:e=openpgp_crypto_hashData(this.algorithm,e+this.salt+a);break a;case f.iterated:var f=[],h=this.get_count();for(data=this.salt+a;f.length*data.lengthh&&(f=f.substr(0,h));e=openpgp_crypto_hashData(this.algorithm,e+f);break a}e=void 0}c+= e;d+=String.fromCharCode(0)}return c.substr(0,b)}}openpgp_type_s2k.type={simple:0,salted:1,iterated:3,gnu:101}; diff --git a/src/encoding/openpgp.encoding.asciiarmor.js b/src/encoding/openpgp.encoding.asciiarmor.js index 84aca0a5..55ed527a 100644 --- a/src/encoding/openpgp.encoding.asciiarmor.js +++ b/src/encoding/openpgp.encoding.asciiarmor.js @@ -23,7 +23,7 @@ * or an object with attribute "text" containing the message text * and an attribute "openpgp" containing the bytes. */ -function openpgp_encoding_deArmor(text) { +function openpgp_encoding_dearmor(text) { text = text.replace(/\r/g, '') var type = openpgp_encoding_get_type(text); diff --git a/src/packet/compressed.js b/src/packet/compressed.js index 8b2c1b40..68765676 100644 --- a/src/packet/compressed.js +++ b/src/packet/compressed.js @@ -153,15 +153,4 @@ function openpgp_packet_compressed() { break; } } - - - /** - * Pretty printing the packet (useful for debug purposes) - * @return {String} - */ - this.toString = function() { - return '5.6. Compressed Data Packet (Tag 8)\n'+ - ' Compression Algorithm = '+this.algorithm+'\n'+ - ' Compressed Data: Byte ['+util.hexstrdump(this.compressed)+']\n'; - } }; diff --git a/src/packet/packet.js b/src/packet/packet.js index fa740453..c6a8b7d4 100644 --- a/src/packet/packet.js +++ b/src/packet/packet.js @@ -121,7 +121,7 @@ function _openpgp_packet() { * @param {integer} len Length of the input from position on * @return {Object} Returns a parsed openpgp_packet */ - function read_packet(input, position, len) { + this.read_packet = function(input, position, len) { // some sanity checks if (input == null || input.length <= position || input.substring(position).length < 2 @@ -300,7 +300,6 @@ function _openpgp_packet() { }; } - this.read_packet = read_packet; /** diff --git a/src/packet/packetlist.js b/src/packet/packetlist.js index 4c3f0a56..0fb7567b 100644 --- a/src/packet/packetlist.js +++ b/src/packet/packetlist.js @@ -7,6 +7,9 @@ * are stored as numerical indices. */ function openpgp_packetlist() { + /** The number of packets contained within the list. + * @readonly + * @type {Integer} */ this.length = 0; diff --git a/src/packet/public_key.js b/src/packet/public_key.js index 1d50ffcd..846bd4e4 100644 --- a/src/packet/public_key.js +++ b/src/packet/public_key.js @@ -27,7 +27,7 @@ function openpgp_packet_public_key() { this.tag = 6; this.version = 4; - this.created = null; + this.created = new Date(); this.mpi = []; this.algorithm = openpgp.publickey.rsa_sign; diff --git a/src/packet/public_key_encrypted_session_key.js b/src/packet/public_key_encrypted_session_key.js index 20f14bed..37e6448e 100644 --- a/src/packet/public_key_encrypted_session_key.js +++ b/src/packet/public_key_encrypted_session_key.js @@ -175,30 +175,5 @@ function openpgp_packet_public_key_encrypted_session_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/secret_key.js b/src/packet/secret_key.js index 3d50210c..a4bab5a2 100644 --- a/src/packet/secret_key.js +++ b/src/packet/secret_key.js @@ -28,11 +28,7 @@ function openpgp_packet_secret_key() { this.tag = 5; this.public_key = new openpgp_packet_public_key(); this.mpi = []; - this.symmetric_algorithm = openpgp.symmetric.plaintext; - this.hash_algorithm = openpgp.hash.sha1; - this.s2k = null; this.encrypted = null; - this.iv = null; function get_hash_len(hash) { @@ -62,7 +58,7 @@ function openpgp_packet_secret_key() { var hash = hashfn(cleartext); if(hash != hashtext) - throw "Hash mismatch!"; + throw new Error("Hash mismatch."); var mpis = openpgp_crypto_getPrivateMpiCount(algorithm); @@ -76,6 +72,19 @@ function openpgp_packet_secret_key() { return mpi; } + function write_cleartext_mpi(hash_algorithm, mpi) { + var bytes= ''; + for(var i in mpi) { + bytes += mpi[i].write(); + } + + + bytes += get_hash_fn(hash_algorithm)(bytes); + + return bytes; + } + + // 5.5.3. Secret-Key Packet Formats /** @@ -96,60 +105,20 @@ function openpgp_packet_secret_key() { // indicates that the secret-key data is not encrypted. 255 or 254 // indicates that a string-to-key specifier is being given. Any // other value is a symmetric-key encryption algorithm identifier. - var s2k_usage = bytes[0].charCodeAt(); - - var i = 1; + var isEncrypted = bytes[0].charCodeAt(); - // - [Optional] If string-to-key usage octet was 255 or 254, a one- - // octet symmetric encryption algorithm. - if (s2k_usage == 255 || s2k_usage == 254) { - this.symmetric_algorithm = bytes[i++].charCodeAt(); - - // - [Optional] If string-to-key usage octet was 255 or 254, a - // string-to-key specifier. The length of the string-to-key - // specifier is implied by its type, as described above. - this.s2k = new openpgp_type_s2k(); - i += this.s2k.read(bytes.substr(i)); - } - - // - [Optional] If secret data is encrypted (string-to-key usage octet - // not zero), an Initial Vector (IV) of the same length as the - // cipher's block size. + if(isEncrypted) { + this.encrypted = bytes; + } else { + + // - Plain or encrypted multiprecision integers comprising the secret + // key data. These algorithm-specific fields are as described + // below. - if (s2k_usage != 0 && s2k_usage != 255 && - s2k_usage != 254) { - this.symmetric_algorithm = s2k_usage; - } - - if (s2k_usage != 0 && this.s2k.type != 1001) { - this.iv = bytes.substr(i, - openpgp_crypto_getBlockLength(this.symmetric_algorithm)); - - i += this.iv.length; - } - - if(s2k_usage == 254) - this.hash_algorithm = openpgp.hash.sha1; - else - this.hash_algorithm = 'checksum'; - - // - Plain or encrypted multiprecision integers comprising the secret - // key data. These algorithm-specific fields are as described - // below. - - // s2k type 1001 corresponds to GPG specific extension without primary key secrets - // http://www.gnupg.org/faq/GnuPG-FAQ.html#how-can-i-use-gnupg-in-an-automated-environment - if (s2k_usage != 0 && this.s2k.type == 1001) { - this.mpi = null; - this.encrypted = null; - - } else if (s2k_usage != 0) { - this.encrypted = bytes.substr(i); - - } else { - this.mpi = parse_cleartext_mpi(this.hash_algorithm, bytes.substr(i), + this.mpi = parse_cleartext_mpi('mod', bytes.substr(1), this.public_key.algorithm); - } + } + } /* @@ -158,7 +127,7 @@ function openpgp_packet_secret_key() { * @param {Integer} keyType Follows the OpenPGP algorithm standard, * IE 1 corresponds to RSA. * @param {RSA.keyObject} key - * @param password + * @param passphrase * @param s2kHash * @param symmetricEncryptionAlgorithm * @param timePacket @@ -168,25 +137,11 @@ function openpgp_packet_secret_key() { this.write = function() { var bytes = this.public_key.write(); - if(this.encrypted == null) { + if(!this.encrypted) { bytes += String.fromCharCode(0); - var mpi = ''; - for(var i in this.mpi) { - mpi += this.mpi[i].write(); - } - - bytes += mpi; - - // TODO check the cheksum! - bytes += openpgp_packet_number_write(util.calc_checksum(mpi), 2); - } else if(this.s2k == null) { - bytes += String.fromCharCode(this.symmetric_algorithm); - bytes += this.encrypted; + bytes += write_cleartext_mpi('mod', this.mpi); } else { - bytes += String.fromCharCode(254); - bytes += String.fromCharCode(this.symmetric_algorithm); - bytes += this.s2k.write(); bytes += this.encrypted; } @@ -196,75 +151,54 @@ function openpgp_packet_secret_key() { - this.encrypt = function(password) { + /** Encrypt the payload. By default, we use aes256 and iterated, salted string + * to key specifier + * @param {String} passphrase + */ + this.encrypt = function(passphrase) { + + var s2k = new openpgp_type_s2k(), + symmetric = openpgp.symmetric.aes256, + cleartext = write_cleartext_mpi(openpgp.hash.sha1, this.mpi), + key = produceEncryptionKey(s2k, passphrase, symmetric), + blockLen = openpgp_crypto_getBlockLength(symmetric), + iv = openpgp_crypto_getRandomBytes(blockLen); - switch(keyType){ - case 1: - body += String.fromCharCode(keyType);//public key algo - body += key.n.toMPI(); - body += key.ee.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.symmetric_algorithm); - //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 cleartext = key.d.toMPI() + key.p.toMPI() + key.q.toMPI() + key.u.toMPI(); - var sha1Hash = str_sha1(cleartext); - 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 = 96; //c of 96 translates to count of 65536 - 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.symmetric_algorithm){ - case 3: - this.iv.length = 8; - this.iv = openpgp_crypto_getRandomBytes(this.iv.length); - ciphertextMPIs = normal_cfb_encrypt(function(block, key) { - var cast5 = new openpgp_symenc_cast5(); - cast5.setKey(key); - return cast5.encrypt(util.str2bin(block)); - }, this.iv.length, util.str2bin(hashKey.substring(0,16)), cleartext + sha1Hash, this.iv); - body += this.iv + ciphertextMPIs; - break; - case 7: - case 8: - case 9: - this.iv.length = 16; - this.iv = openpgp_crypto_getRandomBytes(this.iv.length); - ciphertextMPIs = normal_cfb_encrypt(AESencrypt, - this.iv.length, hashKey, cleartext + 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); - } - var header = openpgp_packet.write_packet_header(tag,body.length); - return {string: header+body , header: header, body: body}; + this.encrypted = ''; + this.encrypted += String.fromCharCode(254); + this.encrypted += String.fromCharCode(symmetric); + this.encrypted += s2k.write(); + this.encrypted += iv; + + console.log(cleartext); + + switch(symmetric) { + case 3: + this.encrypted += normal_cfb_encrypt(function(block, key) { + var cast5 = new openpgp_symenc_cast5(); + cast5.setKey(key); + return cast5.encrypt(util.str2bin(block)); + }, iv.length, key, cleartext, iv); + break; + case 7: + case 8: + case 9: + var fn = function(block,key) { + return AESencrypt(util.str2bin(block),key); + } + this.encrypted += normal_cfb_encrypt(fn, + iv.length, new keyExpansion(key), cleartext, iv); + break; + default: + throw new Error("Unsupported symmetric encryption algorithm."); + } } + function produceEncryptionKey(s2k, passphrase, algorithm) { + return s2k.produce_key(passphrase, + openpgp_crypto_getKeyLength(algorithm)); + } /** * Decrypts the private key MPIs which are needed to use the key. @@ -277,38 +211,65 @@ function openpgp_packet_secret_key() { * @return {Boolean} True if the passphrase was correct; false if not */ this.decrypt = function(passphrase) { - if (this.encrypted == null) + if (!this.encrypted) return; - // creating a key out of the passphrase - var key = this.s2k.produce_key(passphrase, - openpgp_crypto_getKeyLength(this.symmetric_algorithm)); + var i = 0, + symmetric, + key; - var cleartext = ''; + var s2k_usage = this.encrypted[i++].charCodeAt(); + + // - [Optional] If string-to-key usage octet was 255 or 254, a one- + // octet symmetric encryption algorithm. + if (s2k_usage == 255 || s2k_usage == 254) { + symmetric = this.encrypted[i++].charCodeAt(); + + // - [Optional] If string-to-key usage octet was 255 or 254, a + // string-to-key specifier. The length of the string-to-key + // specifier is implied by its type, as described above. + var s2k = new openpgp_type_s2k(); + i += s2k.read(this.encrypted.substr(i)); + + key = produceEncryptionKey(s2k, passphrase, symmetric); + } else { + symmetric = s2k_usage; + key = MD5(passphrase); + } + + // - [Optional] If secret data is encrypted (string-to-key usage octet + // not zero), an Initial Vector (IV) of the same length as the + // cipher's block size. + var iv = this.encrypted.substr(i, + openpgp_crypto_getBlockLength(symmetric)); + + i += iv.length; + + var cleartext, + ciphertext = this.encrypted.substr(i); - switch (this.symmetric_algorithm) { + switch (symmetric) { case 1: // - IDEA [IDEA] - util.print_error("openpgp.packet.keymaterial.js\n" - +"symmetric encryption algorithim: IDEA is not implemented"); + throw new Error("IDEA is not implemented."); return false; case 2: // - TripleDES (DES-EDE, [SCHNEIER] [HAC] - 168 bit key derived from 192) cleartext = normal_cfb_decrypt(function(block, key) { return des(key, block,1,null,0); - }, this.iv.length, key, this.encrypted, this.iv); + }, iv.length, key, ciphertext, iv); break; case 3: // - CAST5 (128 bit key, as per [RFC2144]) cleartext = normal_cfb_decrypt(function(block, key) { var cast5 = new openpgp_symenc_cast5(); cast5.setKey(key); return cast5.encrypt(util.str2bin(block)); - }, this.iv.length, util.str2bin(key.substring(0,16)), this.encrypted, this.iv); + }, iv.length, util.str2bin(key.substring(0,16)), ciphertext, iv); break; case 4: // - Blowfish (128 bit key, 16 rounds) [BLOWFISH] cleartext = normal_cfb_decrypt(function(block, key) { var blowfish = new Blowfish(key); return blowfish.encrypt(block); - }, this.iv.length, key, this.encrypted, this.iv); + }, iv.length, key, ciphertext, iv); break; case 7: // - AES with 128-bit key [AES] case 8: // - AES with 192-bit key @@ -316,27 +277,27 @@ function openpgp_packet_secret_key() { cleartext = normal_cfb_decrypt(function(block,key){ return AESencrypt(util.str2bin(block),key); }, - this.iv.length, keyExpansion(key), - this.encrypted, this.iv); + iv.length, new keyExpansion(key), + ciphertext, iv); break; case 10: // - Twofish with 256-bit key [TWOFISH] - util.print_error("openpgp.packet.keymaterial.js\n"+"Key material is encrypted with twofish: not implemented"); + throw new Error("Twofish is not implemented."); return false; case 5: // - Reserved case 6: // - Reserved default: - util.print_error("openpgp.packet.keymaterial.js\n"+"unknown encryption algorithm for secret key :"+this.symmetric_algorithm); + throw new Error("Unknown symmetric algorithm."); return false; } - - if (cleartext == null) { - util.print_error("openpgp.packet.keymaterial.js\n"+"cleartext was null"); - return false; - } - + + var hash; + if(s2k_usage == 254) + hash = openpgp.hash.sha1; + else + hash = 'mod'; - - this.mpi = parse_cleartext_mpi(this.hash_algorithm, cleartext, + + this.mpi = parse_cleartext_mpi(hash, cleartext, this.public_key.algorithm); } diff --git a/src/packet/sym_encrypted_integrity_protected.js b/src/packet/sym_encrypted_integrity_protected.js index 8086cb92..1d1da74a 100644 --- a/src/packet/sym_encrypted_integrity_protected.js +++ b/src/packet/sym_encrypted_integrity_protected.js @@ -126,19 +126,4 @@ function openpgp_packet_sym_encrypted_integrity_protected() { else this.packets.read(decrypted.substr(0, decrypted.length - 22)); } - - this.toString = function() { - var data = ''; - if(openpgp.config.debug) - data = ' data: Bytes [' - + util.hexstrdump(this.encrypted) + ']'; - - return '5.13. Sym. Encrypted Integrity Protected Data Packet (Tag 18)\n' - + '\n' - + ' version: ' - + this.version - + '\n' - + data; - } - }; diff --git a/src/packet/sym_encrypted_session_key.js b/src/packet/sym_encrypted_session_key.js index 7ab534c1..2544d898 100644 --- a/src/packet/sym_encrypted_session_key.js +++ b/src/packet/sym_encrypted_session_key.js @@ -127,20 +127,5 @@ function openpgp_packet_sym_encrypted_session_key() { openpgp_crypto_getPrefixRandom(this.private_algorithm), this.private_algorithm, key, private_key, true); } - - /** - * 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 2e1902c6..865e577f 100644 --- a/src/packet/symmetrically_encrypted.js +++ b/src/packet/symmetrically_encrypted.js @@ -29,6 +29,7 @@ function openpgp_packet_symmetrically_encrypted() { this.tag = 9; this.encrypted = null; + /** Decrypted packets contained within. */ this.packets = new openpgp_packetlist(); @@ -64,12 +65,4 @@ function openpgp_packet_symmetrically_encrypted() { this.encrypted = openpgp_crypto_symmetricEncrypt( openpgp_crypto_getPrefixRandom(algo), algo, key, data, true); } - - - this.toString = function () { - return '5.7. Symmetrically Encrypted Data Packet (Tag 9)\n' - + ' Used symmetric algorithm: ' + this.algorithmType + '\n' - + ' encrypted data: Bytes [' - + util.hexstrdump(this.encryptedData) + ']\n'; - } }; diff --git a/src/type/s2k.js b/src/type/s2k.js index a7d332f1..4a26b12a 100644 --- a/src/type/s2k.js +++ b/src/type/s2k.js @@ -29,7 +29,7 @@ function openpgp_type_s2k() { this.algorithm = openpgp.hash.sha256; /** @type {openpgp_type_s2k.type} */ this.type = openpgp_type_s2k.type.iterated; - this.c = 10; + this.c = 96; /** @type {openpgp_bytearray} * Eight bytes of salt. */ this.salt = openpgp_crypto_getRandomBytes(8); diff --git a/test/general/packet.js b/test/general/packet.js index d7fc99cf..ef023f62 100644 --- a/test/general/packet.js +++ b/test/general/packet.js @@ -100,7 +100,7 @@ unittests.register("Packet testing", function() { - var msgbytes = openpgp_encoding_deArmor(msg).openpgp; + var msgbytes = openpgp_encoding_dearmor(msg).openpgp; var parsed = new openpgp_packetlist(); parsed.read(msgbytes); @@ -181,7 +181,7 @@ unittests.register("Packet testing", function() { '-----END PGP PRIVATE KEY BLOCK-----'; key = new openpgp_packetlist(); - key.read(openpgp_encoding_deArmor(armored_key).openpgp); + key.read(openpgp_encoding_dearmor(armored_key).openpgp); key = key[0]; var enc = new openpgp_packet_public_key_encrypted_session_key(), @@ -249,11 +249,11 @@ unittests.register("Packet testing", function() { var key = new openpgp_packetlist(); - key.read(openpgp_encoding_deArmor(armored_key).openpgp); + key.read(openpgp_encoding_dearmor(armored_key).openpgp); key = key[3]; var msg = new openpgp_packetlist(); - msg.read(openpgp_encoding_deArmor(armored_msg).openpgp); + msg.read(openpgp_encoding_dearmor(armored_msg).openpgp); msg[0].decrypt(key.public_key.mpi, key.mpi); msg[1].decrypt(msg[0].symmetric_algorithm, msg[0].symmetric_key); @@ -310,12 +310,12 @@ unittests.register("Packet testing", function() { '-----END PGP MESSAGE-----'; var key = new openpgp_packetlist(); - key.read(openpgp_encoding_deArmor(armored_key).openpgp); + key.read(openpgp_encoding_dearmor(armored_key).openpgp); key = key[3]; key.decrypt('test'); var msg = new openpgp_packetlist(); - msg.read(openpgp_encoding_deArmor(armored_msg).openpgp); + msg.read(openpgp_encoding_dearmor(armored_msg).openpgp); msg[0].decrypt(key.public_key.mpi, key.mpi); msg[1].decrypt(msg[0].symmetric_algorithm, msg[0].symmetric_key); @@ -330,7 +330,7 @@ unittests.register("Packet testing", function() { var key = new openpgp_packetlist(); - key.read(openpgp_encoding_deArmor(armored_key).openpgp); + key.read(openpgp_encoding_dearmor(armored_key).openpgp); var verified = key[2].verify(key[0].public_key, @@ -339,7 +339,7 @@ unittests.register("Packet testing", function() { key: key[0].public_key }); - verified = verified == key[4].verify(key[0].public_key, + verified = verified && key[4].verify(key[0].public_key, { key: key[0].public_key, bind: key[3].public_key @@ -367,11 +367,11 @@ unittests.register("Packet testing", function() { '-----END PGP MESSAGE-----' var key = new openpgp_packetlist(); - key.read(openpgp_encoding_deArmor(armored_key).openpgp); + key.read(openpgp_encoding_dearmor(armored_key).openpgp); key[3].decrypt('test') var msg = new openpgp_packetlist(); - msg.read(openpgp_encoding_deArmor(armored_msg).openpgp); + msg.read(openpgp_encoding_dearmor(armored_msg).openpgp); msg[0].decrypt(key[3].public_key.mpi, key[3].mpi); @@ -390,6 +390,40 @@ unittests.register("Packet testing", function() { return new test_result('Reading a signed, encrypted message.', verified == true); + }, function() { + var key = new openpgp_packetlist(); + key.push(new openpgp_packet_secret_key); + + var rsa = new RSA(), + mpi = rsa.generate(512, "10001") + + + var mpi = [ + [mpi.d, mpi.p, mpi.q, mpi.u], + [mpi.n, mpi.ee]]; + + mpi = mpi.map(function(k) { + return k.map(function(bn) { + var mpi = new openpgp_type_mpi(); + mpi.fromBigInteger(bn); + return mpi; + }); + }); + + key[0].public_key.mpi = mpi[1]; + key[0].mpi = mpi[0]; + + key[0].encrypt('hello'); + + var raw = key.write(); + + var key2 = new openpgp_packetlist(); + key2.read(raw); + key2[0].decrypt('hello'); + + + return new test_result('Writing and encryptio of a secret key packet.', + key[0].mpi.toString() == key2[0].mpi.toString()); }];