From c64c75bf043cf28b2b67221e6c44aba11572a801 Mon Sep 17 00:00:00 2001 From: Michal Kolodziej Date: Fri, 10 May 2013 19:09:24 +0200 Subject: [PATCH] Merged public and secret keys where appropriate. --- resources/openpgp.js | 366 +++++++----------- resources/openpgp.min.js | 60 ++- src/ciphers/openpgp.crypto.js | 70 +++- src/openpgp.msg.privatekey.js | 194 +++------- src/packet/public_key.js | 44 +-- .../public_key_encrypted_session_key.js | 13 +- src/packet/secret_key.js | 23 +- src/packet/signature.js | 20 +- test/general/packet.js | 68 ++-- 9 files changed, 336 insertions(+), 522 deletions(-) diff --git a/resources/openpgp.js b/resources/openpgp.js index 539d4754..c6f971ef 100644 --- a/resources/openpgp.js +++ b/resources/openpgp.js @@ -3710,25 +3710,26 @@ function openpgp_crypto_asymetricEncrypt(algo, publicMPIs, data) { * @return {openpgp_type_mpi} returns a big integer containing the decrypted data; otherwise null */ -function openpgp_crypto_asymetricDecrypt(algo, publicMPIs, secretMPIs, dataMPIs) { +function openpgp_crypto_asymetricDecrypt(algo, keyIntegers, dataIntegers) { var bn = (function() { switch(algo) { case 1: // RSA (Encrypt or Sign) [HAC] case 2: // RSA Encrypt-Only [HAC] case 3: // RSA Sign-Only [HAC] var rsa = new RSA(); - var d = secretMPIs[0].toBigInteger(); - var p = secretMPIs[1].toBigInteger(); - var q = secretMPIs[2].toBigInteger(); - var u = secretMPIs[3].toBigInteger(); - var m = dataMPIs[0].toBigInteger(); + // 0 and 1 are the public key. + var d = keyIntegers[2].toBigInteger(); + var p = keyIntegers[3].toBigInteger(); + var q = keyIntegers[4].toBigInteger(); + var u = keyIntegers[5].toBigInteger(); + var m = dataIntegers[0].toBigInteger(); return rsa.decrypt(m, d, p, q, u); case 16: // Elgamal (Encrypt-Only) [ELGAMAL] [HAC] var elgamal = new Elgamal(); - var x = secretMPIs[0].toBigInteger(); - var c1 = dataMPIs[0].toBigInteger(); - var c2 = dataMPIs[1].toBigInteger(); - var p = publicMPIs[0].toBigInteger(); + var x = keyIntegers[3].toBigInteger(); + var c1 = dataIntegers[0].toBigInteger(); + var c2 = dataIntegers[1].toBigInteger(); + var p = keyIntegers[0].toBigInteger(); return elgamal.decrypt(c1,c2,p,x); default: return null; @@ -3763,6 +3764,33 @@ function openpgp_crypto_getPrivateMpiCount(algo) { } else return 0; } + +function openpgp_crypto_getPublicMpiCount(algorithm) { + // - A series of multiprecision integers comprising the key material: + // Algorithm-Specific Fields for RSA public keys: + // - a multiprecision integer (MPI) of RSA public modulus n; + // - an MPI of RSA public encryption exponent e. + if (algorithm > 0 && algorithm < 4) + return 2; + + // Algorithm-Specific Fields for Elgamal public keys: + // - MPI of Elgamal prime p; + // - MPI of Elgamal group generator g; + // - MPI of Elgamal public key value y (= g**x mod p where x is secret). + else if (algorithm == 16) + return 3; + + // Algorithm-Specific Fields for DSA public keys: + // - MPI of DSA prime p; + // - MPI of DSA group order q (q is a prime divisor of p-1); + // - MPI of DSA group generator g; + // - MPI of DSA public-key value y (= g**x mod p where x is secret). + else if (algorithm == 17) + return 4; + else + return 0; +}; + /** * generate random byte prefix as string for the specified algorithm @@ -3927,26 +3955,28 @@ function openpgp_crypto_verifySignature(algo, hash_algo, msg_MPIs, publickey_MPI * @param {String} data Data to be signed * @return {openpgp_type_mpi[]} */ -function openpgp_crypto_signData(hash_algo, algo, publicMPIs, secretMPIs, data) { +function openpgp_crypto_signData(hash_algo, algo, keyIntegers, data) { switch(algo) { case 1: // RSA (Encrypt or Sign) [HAC] case 2: // RSA Encrypt-Only [HAC] case 3: // RSA Sign-Only [HAC] var rsa = new RSA(); - var d = secretMPIs[0].toBigInteger(); - var n = publicMPIs[0].toBigInteger(); - var m = openpgp_encoding_emsa_pkcs1_encode(hash_algo, data,publicMPIs[0].byteLength()); + var d = keyIntegers[2].toBigInteger(); + var n = keyIntegers[0].toBigInteger(); + var m = openpgp_encoding_emsa_pkcs1_encode(hash_algo, + data, keyIntegers[0].byteLength()); + util.print_debug("signing using RSA"); return rsa.sign(m, d, n).toMPI(); case 17: // DSA (Digital Signature Algorithm) [FIPS186] [HAC] var dsa = new DSA(); - util.print_debug("DSA Sign: q size in Bytes:"+publicMPIs[1].getByteLength()); - var p = publicMPIs[0].toBigInteger(); - var q = publicMPIs[1].toBigInteger(); - var g = publicMPIs[2].toBigInteger(); - var y = publicMPIs[3].toBigInteger(); - var x = secretMPIs[0].toBigInteger(); + util.print_debug("DSA Sign: q size in Bytes:"+keyIntegers[1].getByteLength()); + var p = keyIntegers[0].toBigInteger(); + var q = keyIntegers[1].toBigInteger(); + var g = keyIntegers[2].toBigInteger(); + var y = keyIntegers[3].toBigInteger(); + var x = keyIntegers[4].toBigInteger(); var m = data; var result = dsa.sign(hash_algo,m, g, p, q, x); util.print_debug("signing using DSA\n result:"+util.hexstrdump(result[0])+"|"+util.hexstrdump(result[1])); @@ -7449,7 +7479,7 @@ function openpgp_config() { keyserver: "keyserver.linux.it" // "pgp.mit.edu:11371" }; - this.versionstring ="OpenPGP.js v.1.20130509"; + this.versionstring ="OpenPGP.js v.1.20130510"; this.commentstring ="http://openpgpjs.org"; /** * Reads the config out of the HTML5 local storage @@ -8898,98 +8928,55 @@ function openpgp_msg_message() { /** * @class - * @classdesc Class that represents a decoded private key for internal openpgp.js use + * @classdesc Class that represents an OpenPGP key. Must contain a master key. + * Can contain additional subkeys, signatures, + * user ids, user attributes. */ -function openpgp_msg_privatekey() { - this.subKeys = new Array(); - this.privateKeyPacket = null; - this.userIds = new Array(); - this.userAttributes = new Array(); - this.revocationSignatures = new Array(); - this.subKeys = new Array(); +function openpgp_key() { + this.packets = new openpgp_packetlist(); - /** - * - * @return last position - */ - function read_nodes(parent_node, input, position, len) { - this.privateKeyPacket = parent_node; + /** Returns the master key (secret or public) + * @returns {openpgp_packet_secret_key|openpgp_packet_public_key|null} */ + this.getKey = function() { + for(var i = 0; i < this.packets.length; i++) + if(this.packets[i] instanceof openpgp_packet_secret_key || + this.packets[i] instanceof openpgp_packet_public_key) + return this.packets[i]; + + return null; + } + + /** Returns all the private and public subkeys + * @returns {openpgp_packet_subkey[]} */ + this.getSubkeys = function() { + + var subkeys = []; + + for(var i = 0; i < this.packets.length; i++) + if(this.packets[i] instanceof openpgp_packet_secret_subkey || + this.packets[i] instanceof openpgp_packet_public_subkey) + subkeys.push(this.packets[i]); + + return subkeys; + } + + this.getAllKeys = function() { + return [this.getKey()].concat(this.getSubkeys()); + } + + + this.getSigningKey = function() { - var pos = position; - while (input.length > pos) { - var result = openpgp_packet.read_packet(input, pos, input.length - pos); - if (result == null) { - util.print_error("openpgp.msg.messge decrypt:\n"+'[pub/priv_key]parsing ends here @:' + pos + " l:" + len); - break; - } else { - switch (result.tagType) { - case 2: // public key revocation signature - if (result.signatureType == 32) - this.revocationSignatures[this.revocationSignatures.length] = result; - else if (result.signatureType > 15 && result.signatureType < 20) { - if (this.certificationsignatures == null) - this.certificationSignatures = new Array(); - this.certificationSignatures[this.certificationSignatures.length] = result; - } else - util.print_error("openpgp.msg.messge decrypt:\n"+"unknown signature type directly on key "+result.signatureType+" @"+pos); - pos += result.packetLength + result.headerLength; - break; - case 7: // PrivateSubkey Packet - this.subKeys[this.subKeys.length] = result; - pos += result.packetLength + result.headerLength; - pos += result.read_nodes(this.privateKeyPacket,input, pos, input.length - pos); - break; - case 17: // User Attribute Packet - this.userAttributes[this.userAttributes.length] = result; - pos += result.packetLength + result.headerLength; - pos += result.read_nodes(this.privateKeyPacket,input, pos, input.length - pos); - break; - case 13: // User ID Packet - this.userIds[this.userIds.length] = result; - pos += result.packetLength + result.headerLength; - pos += result.read_nodes(this.privateKeyPacket, input, pos, input.length - pos); - break; - default: - this.position = position - this.privateKeyPacket.packetLength - this.privateKeyPacket.headerLength; - this.len = pos - position; - return this.len; - } - } - } - this.position = position - this.privateKeyPacket.packetLength - this.privateKeyPacket.headerLength; - this.len = pos - position; - - return this.len; - } - - function getKeyId() { - return this.privateKeyPacket.publicKey.getKeyId(); - } - - - function getSubKeyIds() { - if (this.privateKeyPacket.publicKey.version == 4) // V3 keys MUST NOT have subkeys. - var result = new Array(); - for (var i = 0; i < this.subKeys.length; i++) { - result[i] = str_sha1(this.subKeys[i].publicKey.header+this.subKeys[i].publicKey.data).substring(12,20); - } - return result; - } - - - function getSigningKey() { - if ((this.privateKeyPacket.publicKey.publicKeyAlgorithm == 17 || - this.privateKeyPacket.publicKey.publicKeyAlgorithm != 2) - && this.privateKeyPacket.publicKey.verifyKey() == 3) - return this.privateKeyPacket; - else if (this.privateKeyPacket.publicKey.version == 4) // V3 keys MUST NOT have subkeys. - for (var j = 0; j < this.privateKeyPacket.subKeys.length; j++) { - if ((this.privateKeyPacket.subKeys[j].publicKey.publicKeyAlgorithm == 17 || - this.privateKeyPacket.subKeys[j].publicKey.publicKeyAlgorithm != 2) && - this.privateKeyPacket.subKeys[j].publicKey.verifyKey() == 3) - return this.privateKeyPacket.subKeys[j]; - } + var signing = ['rsa_encrypt_sign', 'rsa_sign', 'dsa']; + signing = signing.map(function(s) { return openpgp.publickey[s]; }) + + var keys = this.getAllKeys(); + + for(var i in keys) + if(signing.indexOf(keys[i].public_algorithm) != -1) + return keys[i]; + return null; } @@ -9009,67 +8996,22 @@ function openpgp_msg_privatekey() { } - function decryptSecretMPIs(str_passphrase) { - return this.privateKeyPacket.decryptSecretMPIs(str_passphrase); + this.decrypt = function(passphrase) { + var keys = this.getAllKeys(); + + for(var i in keys) + if(keys[i] instanceof openpgp_packet_secret_subkey || + keys[i] instanceof openpgp_packet_secret_key) + + keys[i].decrypt(passphrase); } - function getFingerprint() { - return this.privateKeyPacket.publicKey.getFingerprint(); - } // TODO need to implement this function revoke() { } - /** - * extracts the public key part - * @return {String} OpenPGP armored text containing the public key - * returns null if no sufficient data to extract public key - */ - function extractPublicKey() { - // add public key - var key = this.privateKeyPacket.publicKey.header + this.privateKeyPacket.publicKey.data; - for (var i = 0; i < this.userIds.length; i++) { - // verify userids - if (this.userIds[i].certificationSignatures.length === 0) { - util.print_error("extractPublicKey - missing certification signatures"); - return null; - } - var userIdPacket = new openpgp_packet_userid(); - // add userids - key += userIdPacket.write_packet(this.userIds[i].text); - for (var j = 0; j < this.userIds[i].certificationSignatures.length; j++) { - var certSig = this.userIds[i].certificationSignatures[j]; - // add signatures - key += openpgp_packet.write_packet_header(2, certSig.data.length) + certSig.data; - } - } - for (var k = 0; k < this.subKeys.length; k++) { - var pubSubKey = this.subKeys[k].publicKey; - // add public subkey package - key += openpgp_packet.write_old_packet_header(14, pubSubKey.data.length) + pubSubKey.data; - var subKeySig = this.subKeys[k].subKeySignature; - if (subKeySig !== null) { - // add subkey signature - key += openpgp_packet.write_packet_header(2, subKeySig.data.length) + subKeySig.data; - } else { - util.print_error("extractPublicKey - missing subkey signature"); - return null; - } - } - var publicArmored = openpgp_encoding_armor(4, key); - return publicArmored; - } - - this.extractPublicKey = extractPublicKey; - this.getSigningKey = getSigningKey; - this.getFingerprint = getFingerprint; - this.getPreferredSignatureHashAlgorithm = getPreferredSignatureHashAlgorithm; - this.read_nodes = read_nodes; - this.decryptSecretMPIs = decryptSecretMPIs; - this.getSubKeyIds = getSubKeyIds; - this.getKeyId = getKeyId; } // GPG4Browsers - An OpenPGP implementation in javascript @@ -10312,7 +10254,7 @@ function openpgp_packet_public_key_encrypted_session_key() { return result; } - this.encrypt = function(public_key_mpi) { + this.encrypt = function(key) { var data = String.fromCharCode(this.symmetric_algorithm); data += this.symmetric_key; @@ -10323,11 +10265,11 @@ function openpgp_packet_public_key_encrypted_session_key() { var mpi = new openpgp_type_mpi(); mpi.fromBytes(openpgp_encoding_eme_pkcs1_encode( data, - public_key_mpi[0].byteLength())); + key.mpi[0].byteLength())); this.encrypted = openpgp_crypto_asymetricEncrypt( this.public_key_algorithm, - public_key_mpi, + key.mpi, mpi); } @@ -10341,11 +10283,10 @@ function openpgp_packet_public_key_encrypted_session_key() { * Private key with secMPIs unlocked * @return {String} The unencrypted session key */ - this.decrypt = function(public_key_mpi, private_key_mpi) { + this.decrypt = function(key) { var result = openpgp_crypto_asymetricDecrypt( this.public_key_algorithm, - public_key_mpi, - private_key_mpi, + key.mpi, this.encrypted).toBytes(); var checksum = ((result.charCodeAt(result.length - 2) << 8) @@ -10353,7 +10294,7 @@ function openpgp_packet_public_key_encrypted_session_key() { var decoded = openpgp_encoding_eme_pkcs1_decode( result, - public_key_mpi[0].byteLength()); + key.mpi[0].byteLength()); var key = decoded.substring(1, decoded.length - 2); @@ -10395,37 +10336,10 @@ function openpgp_packet_public_key_encrypted_session_key() { */ function openpgp_packet_public_key() { this.tag = 6; - this.version = 4; this.created = new Date(); this.mpi = []; this.algorithm = openpgp.publickey.rsa_sign; - - var public_mpis = function(algorithm) { - // - A series of multiprecision integers comprising the key material: - // Algorithm-Specific Fields for RSA public keys: - // - a multiprecision integer (MPI) of RSA public modulus n; - // - an MPI of RSA public encryption exponent e. - if (algorithm > 0 && algorithm < 4) - return 2; - // Algorithm-Specific Fields for Elgamal public keys: - // - MPI of Elgamal prime p; - // - MPI of Elgamal group generator g; - // - MPI of Elgamal public key value y (= g**x mod p where x is secret). - else if (algorithm == 16) - return 3; - - // Algorithm-Specific Fields for DSA public keys: - // - MPI of DSA prime p; - // - MPI of DSA group order q (q is a prime divisor of p-1); - // - MPI of DSA group generator g; - // - MPI of DSA public-key value y (= g**x mod p where x is secret). - else if (algorithm == 17) - return 4; - else - return 0; - }; - /** * Internal Parser for public keys as specified in RFC 4880 section @@ -10436,11 +10350,11 @@ function openpgp_packet_public_key() { * @param {Integer} len Length of the packet or remaining length of input * @return {Object} This object with attributes set by the parser */ - this.read = function(bytes) { + this.readPublicKey = this.read = function(bytes) { // A one-octet version number (3 or 4). - this.version = bytes[0].charCodeAt(); + var version = bytes[0].charCodeAt(); - if (this.version == 3) { + if (version == 3) { /* // A four-octet number denoting the time that the key was created. this.creationTime = new Date(((input[mypos++].charCodeAt() << 24) | @@ -10489,14 +10403,14 @@ function openpgp_packet_public_key() { } this.packetLength = mypos-position; */ - } else if (this.version == 4) { + } else if (version == 4) { // - A four-octet number denoting the time that the key was created. this.created = openpgp_packet_time_read(bytes.substr(1, 4)); // - A one-octet number denoting the public-key algorithm of this key. this.algorithm = bytes[5].charCodeAt(); - var mpicount = public_mpis(this.algorithm); + var mpicount = openpgp_crypto_getPublicMpiCount(this.algorithm); this.mpi = []; var bmpi = bytes.substr(6); @@ -10531,12 +10445,15 @@ function openpgp_packet_public_key() { * @return {Object} {body: [string]OpenPGP packet body contents, * header: [string] OpenPGP packet header, string: [string] header+body} */ - this.write = function() { + this.writePublicKey = this.write = function() { + // Version var result = String.fromCharCode(4); result += openpgp_packet_time_write(this.created); result += String.fromCharCode(this.algorithm); - for(var i in this.mpi) { + var mpicount = openpgp_crypto_getPublicMpiCount(this.algorithm); + + for(var i = 0; i < mpicount; i++) { result += this.mpi[i].write(); } @@ -10575,9 +10492,9 @@ function openpgp_packet_public_subkey() { * major versions. Consequently, this section is complex. */ function openpgp_packet_secret_key() { + openpgp_packet_public_key.call(this); + this.tag = 5; - this.public_key = new openpgp_packet_public_key(); - this.mpi = []; this.encrypted = null; @@ -10624,7 +10541,9 @@ function openpgp_packet_secret_key() { function write_cleartext_mpi(hash_algorithm, mpi) { var bytes= ''; - for(var i in mpi) { + var discard = openpgp_crypto_getPublicMpiCount(this.algorithm); + + for(var i = discard; i < mpi.length; i++) { bytes += mpi[i].write(); } @@ -10646,7 +10565,7 @@ function openpgp_packet_secret_key() { */ this.read = function(bytes) { // - A Public-Key or Public-Subkey packet, as described above. - var len = this.public_key.read(bytes); + var len = this.readPublicKey(bytes); bytes = bytes.substr(len); @@ -10665,8 +10584,8 @@ function openpgp_packet_secret_key() { // key data. These algorithm-specific fields are as described // below. - this.mpi = parse_cleartext_mpi('mod', bytes.substr(1), - this.public_key.algorithm); + this.mpi = this.mpi.concat(parse_cleartext_mpi('mod', bytes.substr(1), + this.algorithm)); } } @@ -10685,7 +10604,7 @@ function openpgp_packet_secret_key() { header: [string] OpenPGP packet header, string: [string] header+body} */ this.write = function() { - var bytes = this.public_key.write(); + var bytes = this.writePublicKey(); if(!this.encrypted) { bytes += String.fromCharCode(0); @@ -10847,8 +10766,8 @@ function openpgp_packet_secret_key() { hash = 'mod'; - this.mpi = parse_cleartext_mpi(hash, cleartext, - this.public_key.algorithm); + this.mpi = this.mpi.concat(parse_cleartext_mpi(hash, cleartext, + this.algorithm)); } /** @@ -10882,13 +10801,14 @@ function openpgp_packet_secret_key() { } } +openpgp_packet_secret_key.prototype = new openpgp_packet_public_key(); + function openpgp_packet_secret_subkey() { openpgp_packet_secret_key.call(this); this.tag = 7; } - // GPG4Browsers - An OpenPGP implementation in javascript // Copyright (C) 2011 Recurity Labs GmbH // @@ -10921,7 +10841,6 @@ function openpgp_packet_signature() { this.signatureType = null; this.hashAlgorithm = null; this.publicKeyAlgorithm = null; - this.version = 4; this.signatureData = null; this.signedHashValue = null; @@ -10971,9 +10890,9 @@ function openpgp_packet_signature() { this.read = function(bytes) { var i = 0; - this.version = bytes[i++].charCodeAt(); + var version = bytes[i++].charCodeAt(); // switch on version (3 and 4) - switch (this.version) { + switch (version) { case 3: // One-octet length of following hashed material. MUST be 5. if (bytes[i++].charCodeAt() != 5) @@ -11072,8 +10991,7 @@ function openpgp_packet_signature() { * @param {Object} data Contains packets to be signed. * @param {openpgp_msg_privatekey} privatekey private key used to sign the message. */ - this.sign = function(privatekey, data) { - var publickey = privatekey.public_key; + this.sign = function(key, data) { var result = String.fromCharCode(4); result += String.fromCharCode(this.signatureType); @@ -11089,14 +11007,15 @@ function openpgp_packet_signature() { var trailer = this.calculateTrailer(); - var toHash = this.toSign(this.signatureType, data) + this.signatureData + trailer; + var toHash = this.toSign(this.signatureType, data) + + this.signatureData + trailer; var hash = openpgp_crypto_hashData(this.hashAlgorithm, toHash); this.signedHashValue = hash.substr(0, 2); - this.signature = openpgp_crypto_signData(this.hashAlgorithm, this.publicKeyAlgorithm, - publickey.mpi, privatekey.mpi, toHash); + this.signature = openpgp_crypto_signData(this.hashAlgorithm, + this.publicKeyAlgorithm, key.mpi, toHash); } /** @@ -11251,6 +11170,7 @@ function openpgp_packet_signature() { } }; + // Produces data to produce signature on this.toSign = function(type, data) { var t = openpgp_packet_signature.type; @@ -11304,7 +11224,7 @@ function openpgp_packet_signature() { if(data.key == undefined) throw new Error('Key packet is required for this sigtature.'); - var bytes = data.key.write(); + var bytes = data.key.writePublicKey(); return String.fromCharCode(0x99) + openpgp_packet_number_write(bytes.length, 2) + @@ -11327,7 +11247,7 @@ function openpgp_packet_signature() { this.calculateTrailer = function() { // calculating the trailer var trailer = ''; - trailer += String.fromCharCode(this.version); + trailer += String.fromCharCode(4); // Version trailer += String.fromCharCode(0xFF); trailer += openpgp_packet_number_write(this.signatureData.length, 4); return trailer diff --git a/resources/openpgp.min.js b/resources/openpgp.min.js index dd269657..d27ff860 100644 --- a/resources/openpgp.min.js +++ b/resources/openpgp.min.js @@ -108,13 +108,13 @@ k.join("");if(e){for(g=0;gb*h;){for(var e=a(f,c),f=d.substring(h*b,h*b+b),k=0;kb*h;){for(var j=a(f,c),f=d.substring(h*b+0,h*b+b+0),e=0;ea?4:16==a?1:17==a?1:0}function openpgp_crypto_getPrefixRandom(a){switch(a){case 2:case 3:case 4:return openpgp_crypto_getRandomBytes(8);case 7:case 8:case 9:case 10:return openpgp_crypto_getRandomBytes(16);default:return null}} +function openpgp_crypto_asymetricDecrypt(a,b,c){var d=function(){switch(a){case 1:case 2:case 3:var d=new RSA,e=b[2].toBigInteger(),g=b[3].toBigInteger(),j=b[4].toBigInteger(),k=b[5].toBigInteger(),l=c[0].toBigInteger();return d.decrypt(l,e,g,j,k);case 16:return d=new Elgamal,e=b[3].toBigInteger(),j=c[0].toBigInteger(),k=c[1].toBigInteger(),g=b[0].toBigInteger(),d.decrypt(j,k,g,e);default:return null}}(),e=new openpgp_type_mpi;e.fromBigInteger(d);return e} +function openpgp_crypto_getPrivateMpiCount(a){return 0a?4:16==a?1:17==a?1:0}function openpgp_crypto_getPublicMpiCount(a){return 0a?2:16==a?3:17==a?4:0}function openpgp_crypto_getPrefixRandom(a){switch(a){case 2:case 3:case 4:return openpgp_crypto_getRandomBytes(8);case 7:case 8:case 9:case 10:return openpgp_crypto_getRandomBytes(16);default:return null}} function openpgp_crypto_MDCSystemBytes(a,b,c){util.print_debug_hexstr_dump("openpgp_crypto_symmetricDecrypt:\nencrypteddata:",c);switch(a){case 0:return c;case 2:return openpgp_cfb_mdc(desede,8,b,c,openpgp_cfb);case 3:return openpgp_cfb_mdc(cast5_encrypt,8,b,c);case 4:return openpgp_cfb_mdc(BFencrypt,8,b,c);case 7:case 8:case 9:return openpgp_cfb_mdc(AESencrypt,16,keyExpansion(b),c);case 10:return openpgp_cfb_mdc(TFencrypt,16,b,c);case 1:util.print_error(""+(1==a?"IDEA Algorithm not implemented": "Twofish Algorithm not implemented"))}return null}function openpgp_crypto_generateSessionKey(a){return openpgp_crypto_getRandomBytes(openpgp_crypto_getKeyLength(a))}function openpgp_crypto_getKeyLength(a){switch(a){case 2:case 8:return 24;case 3:case 4:case 7:return 16;case 9:case 10:return 32}return null}function openpgp_crypto_getBlockLength(a){switch(a){case 1:case 2:case 3:return 8;case 4:case 7:case 8:case 9:return 16;case 10:return 32;default:return 0}} function openpgp_crypto_verifySignature(a,b,c,d,e){var f=openpgp_crypto_hashData(b,e);switch(a){case 1:case 2:case 3:e=new RSA;a=d[0].toBigInteger();d=d[1].toBigInteger();c=c[0].toBigInteger();d=e.verify(c,d,a);b=openpgp_encoding_emsa_pkcs1_decode(b,d.toMPI().substring(2));return-1==b?(util.print_error("PKCS1 padding in message or key incorrect. Aborting..."),!1):b==f;case 16:return util.print_error("signing with Elgamal is not defined in the OpenPGP standard."),null;case 17:var a=new DSA,f=c[0].toBigInteger(), c=c[1].toBigInteger(),h=d[0].toBigInteger(),g=d[1].toBigInteger(),j=d[2].toBigInteger(),d=d[3].toBigInteger(),d=a.verify(b,f,c,e,h,g,j,d);return 0==d.compareTo(f);default:return null}} -function openpgp_crypto_signData(a,b,c,d,e){switch(b){case 1:case 2:case 3:var b=new RSA,d=d[0].toBigInteger(),f=c[0].toBigInteger(),a=openpgp_encoding_emsa_pkcs1_encode(a,e,c[0].byteLength());util.print_debug("signing using RSA");return b.sign(a,d,f).toMPI();case 17:b=new DSA;util.print_debug("DSA Sign: q size in Bytes:"+c[1].getByteLength());var f=c[0].toBigInteger(),h=c[1].toBigInteger(),g=c[2].toBigInteger();c[3].toBigInteger();c=d[0].toBigInteger();a=b.sign(a,e,g,f,h,c);util.print_debug("signing using DSA\n result:"+ +function openpgp_crypto_signData(a,b,c,d){switch(b){case 1:case 2:case 3:var b=new RSA,e=c[2].toBigInteger(),f=c[0].toBigInteger(),a=openpgp_encoding_emsa_pkcs1_encode(a,d,c[0].byteLength());util.print_debug("signing using RSA");return b.sign(a,e,f).toMPI();case 17:b=new DSA;util.print_debug("DSA Sign: q size in Bytes:"+c[1].getByteLength());var e=c[0].toBigInteger(),f=c[1].toBigInteger(),h=c[2].toBigInteger();c[3].toBigInteger();c=c[4].toBigInteger();a=b.sign(a,d,h,e,f,c);util.print_debug("signing using DSA\n result:"+ util.hexstrdump(a[0])+"|"+util.hexstrdump(a[1]));return a[0].toString()+a[1].toString();case 16:return util.print_debug("signing with Elgamal is not defined in the OpenPGP standard."),null;default:return null}}function openpgp_crypto_hashData(a,b){var c=null;switch(a){case 1:c=MD5(b);break;case 2:c=str_sha1(b);break;case 3:c=RMDstring(b);break;case 8:c=str_sha256(b);break;case 9:c=str_sha384(b);break;case 10:c=str_sha512(b);break;case 11:c=str_sha224(b)}return c} function openpgp_crypto_getHashByteLength(a){switch(a){case 1:return 16;case 2:case 3:return 20;case 8:return 32;case 9:return 48;case 10:return 64;case 11:return 28}return null}function openpgp_crypto_getRandomBytes(a){for(var b="",c=0;cb-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} @@ -285,7 +285,7 @@ JXG.Util.asciiCharCodeAt=function(a,b){var c=a.charCodeAt(b);if(255d?(b.push(String.fromCharCode(d)),c++):191d?(e=a.charCodeAt(c+1),b.push(String.fromCharCode((d&31)<<6|e&63)),c+=2):(e=a.charCodeAt(c+1),f=a.charCodeAt(c+2),b.push(String.fromCharCode((d&15)<<12|(e&63)<<6|f&63)),c+=3);return b.join("")}; JXG.Util.genUUID=function(){for(var a="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".split(""),b=Array(36),c=0,d,e=0;36>e;e++)8==e||13==e||18==e||23==e?b[e]="-":14==e?b[e]="4":(2>=c&&(c=33554432+16777216*Math.random()|0),d=c&15,c>>=4,b[e]=a[19==e?d&3|8:d]);return b.join("")}; -function openpgp_config(){this.config=null;this.default_config={prefer_hash_algorithm:8,encryption_cipher:9,compression:1,show_version:!0,show_comment:!0,integrity_protect:!0,composition_behavior:0,keyserver:"keyserver.linux.it"};this.versionstring="OpenPGP.js v.1.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", +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.20130510";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;){var e=openpgp_packet.read_packet(b,a,b.length-a);if(null==e){util.print_error("openpgp.msg.messge decrypt:\n[pub/priv_key]parsing ends here @:"+a+" l:"+d);break}else switch(e.tagType){case 2:if(32==e.signatureType)this.revocationSignatures[this.revocationSignatures.length]=e;else if(15e.signatureType){if(null==this.certificationsignatures)this.certificationSignatures=[];this.certificationSignatures[this.certificationSignatures.length]=e}else util.print_error("openpgp.msg.messge decrypt:\nunknown signature type directly on key "+ -e.signatureType+" @"+a);a+=e.packetLength+e.headerLength;break;case 7:this.subKeys[this.subKeys.length]=e;a+=e.packetLength+e.headerLength;a+=e.read_nodes(this.privateKeyPacket,b,a,b.length-a);break;case 17:this.userAttributes[this.userAttributes.length]=e;a+=e.packetLength+e.headerLength;a+=e.read_nodes(this.privateKeyPacket,b,a,b.length-a);break;case 13:this.userIds[this.userIds.length]=e;a+=e.packetLength+e.headerLength;a+=e.read_nodes(this.privateKeyPacket,b,a,b.length-a);break;default:return this.position= -c-this.privateKeyPacket.packetLength-this.privateKeyPacket.headerLength,this.len=a-c}}this.position=c-this.privateKeyPacket.packetLength-this.privateKeyPacket.headerLength;return this.len=a-c};this.decryptSecretMPIs=function(a){return this.privateKeyPacket.decryptSecretMPIs(a)};this.getSubKeyIds=function(){if(4==this.privateKeyPacket.publicKey.version)var a=[];for(var b=0;ba.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))}} -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;ga.length&&util.print_error("openpgp.packet.keymaterial.js\nerror reading MPI @:"+ +c);return c+6}util.print_error("Unknown packet version")}};this.writePublicKey=this.write=function(){for(var a=String.fromCharCode(4),a=a+openpgp_packet_time_write(this.created),a=a+String.fromCharCode(this.algorithm),b=openpgp_crypto_getPublicMpiCount(this.algorithm),c=0;cthis.publicKeyAlgorithm){var a=this.MPIs[0].substring(this.MPIs[0].mpiByteLength-8);util.print_debug("openpgp.msg.publickey read_nodes:\nV3 key ID: "+ +b)},g.length,new keyExpansion(a),c,g);break;case 10:throw Error("Twofish is not implemented.");default:throw Error("Unknown symmetric algorithm.");}this.mpi=this.mpi.concat(b(254==h?openpgp.hash.sha1:"mod",f,this.algorithm))}};this.getKeyId=function(){if(4==this.version)return this.getFingerprint().substring(12,20);if(3==this.version&&0this.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.publicKeyAlgorithm=this.hashAlgorithm=this.signatureType=null;this.version=4;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.signatureNeverExpires=this.signatureExpirationTime=this.created=this.mpi=this.signedHashValue=this.signatureData= -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=a[b++].charCodeAt();var c=function(a,b){for(var c=openpgp_packet_number_read(a.substr(0,2)),h=2;h<2+c;){var g=openpgp_packet.read_simple_length(a.substr(h)),h=h+g.offset;b&&this.read_sub_packet(a.substr(h,g.len));h+=g.len}return h},b=b+c.call(this,a.substr(b),!0);this.signatureData=a.substr(0,b);b+=c.call(this,a.substr(b),!1);break;default:util.print_error("openpgp.packet.signature.js\nunknown signature packet version"+this.version)}this.signedHashValue=a.substr(b,2);this.signature= -a.substr(b+2)};this.write=function(){return this.signatureData+openpgp_packet_number_write(0,2)+this.signedHashValue+this.signature};this.sign=function(a,b){var c=a.public_key,d=String.fromCharCode(4),d=d+String.fromCharCode(this.signatureType),d=d+String.fromCharCode(this.publicKeyAlgorithm),d=d+String.fromCharCode(this.hashAlgorithm);this.signatureData=d+=openpgp_packet_number_write(0,2);d=this.calculateTrailer();d=this.toSign(this.signatureType,b)+this.signatureData+d;this.signedHashValue=openpgp_crypto_hashData(this.hashAlgorithm, -d).substr(0,2);this.signature=openpgp_crypto_signData(this.hashAlgorithm,this.publicKeyAlgorithm,c.mpi,a.mpi,d)};this.read_sub_packet=function(a){function b(a,b){this[a]=[];for(var c=0;cthis.publicKeyAlgorithm?e=1:17==this.publicKeyAlgorithm&&(e=2);for(var f=[],h=0,g=0;gthis.publicKeyAlgorithm?e=1:17==this.publicKeyAlgorithm&&(e=2);for(var f=[],h=0,g=0;g 0 && algorithm < 4) + return 2; + + // Algorithm-Specific Fields for Elgamal public keys: + // - MPI of Elgamal prime p; + // - MPI of Elgamal group generator g; + // - MPI of Elgamal public key value y (= g**x mod p where x is secret). + else if (algorithm == 16) + return 3; + + // Algorithm-Specific Fields for DSA public keys: + // - MPI of DSA prime p; + // - MPI of DSA group order q (q is a prime divisor of p-1); + // - MPI of DSA group generator g; + // - MPI of DSA public-key value y (= g**x mod p where x is secret). + else if (algorithm == 17) + return 4; + else + return 0; +}; + /** * generate random byte prefix as string for the specified algorithm @@ -285,26 +313,28 @@ function openpgp_crypto_verifySignature(algo, hash_algo, msg_MPIs, publickey_MPI * @param {String} data Data to be signed * @return {openpgp_type_mpi[]} */ -function openpgp_crypto_signData(hash_algo, algo, publicMPIs, secretMPIs, data) { +function openpgp_crypto_signData(hash_algo, algo, keyIntegers, data) { switch(algo) { case 1: // RSA (Encrypt or Sign) [HAC] case 2: // RSA Encrypt-Only [HAC] case 3: // RSA Sign-Only [HAC] var rsa = new RSA(); - var d = secretMPIs[0].toBigInteger(); - var n = publicMPIs[0].toBigInteger(); - var m = openpgp_encoding_emsa_pkcs1_encode(hash_algo, data,publicMPIs[0].byteLength()); + var d = keyIntegers[2].toBigInteger(); + var n = keyIntegers[0].toBigInteger(); + var m = openpgp_encoding_emsa_pkcs1_encode(hash_algo, + data, keyIntegers[0].byteLength()); + util.print_debug("signing using RSA"); return rsa.sign(m, d, n).toMPI(); case 17: // DSA (Digital Signature Algorithm) [FIPS186] [HAC] var dsa = new DSA(); - util.print_debug("DSA Sign: q size in Bytes:"+publicMPIs[1].getByteLength()); - var p = publicMPIs[0].toBigInteger(); - var q = publicMPIs[1].toBigInteger(); - var g = publicMPIs[2].toBigInteger(); - var y = publicMPIs[3].toBigInteger(); - var x = secretMPIs[0].toBigInteger(); + util.print_debug("DSA Sign: q size in Bytes:"+keyIntegers[1].getByteLength()); + var p = keyIntegers[0].toBigInteger(); + var q = keyIntegers[1].toBigInteger(); + var g = keyIntegers[2].toBigInteger(); + var y = keyIntegers[3].toBigInteger(); + var x = keyIntegers[4].toBigInteger(); var m = data; var result = dsa.sign(hash_algo,m, g, p, q, x); util.print_debug("signing using DSA\n result:"+util.hexstrdump(result[0])+"|"+util.hexstrdump(result[1])); diff --git a/src/openpgp.msg.privatekey.js b/src/openpgp.msg.privatekey.js index b122ae1c..745c2fc0 100644 --- a/src/openpgp.msg.privatekey.js +++ b/src/openpgp.msg.privatekey.js @@ -17,98 +17,55 @@ /** * @class - * @classdesc Class that represents a decoded private key for internal openpgp.js use + * @classdesc Class that represents an OpenPGP key. Must contain a master key. + * Can contain additional subkeys, signatures, + * user ids, user attributes. */ -function openpgp_msg_privatekey() { - this.subKeys = new Array(); - this.privateKeyPacket = null; - this.userIds = new Array(); - this.userAttributes = new Array(); - this.revocationSignatures = new Array(); - this.subKeys = new Array(); +function openpgp_key() { + this.packets = new openpgp_packetlist(); - /** - * - * @return last position - */ - function read_nodes(parent_node, input, position, len) { - this.privateKeyPacket = parent_node; + /** Returns the master key (secret or public) + * @returns {openpgp_packet_secret_key|openpgp_packet_public_key|null} */ + this.getKey = function() { + for(var i = 0; i < this.packets.length; i++) + if(this.packets[i] instanceof openpgp_packet_secret_key || + this.packets[i] instanceof openpgp_packet_public_key) + return this.packets[i]; + + return null; + } + + /** Returns all the private and public subkeys + * @returns {openpgp_packet_subkey[]} */ + this.getSubkeys = function() { + + var subkeys = []; + + for(var i = 0; i < this.packets.length; i++) + if(this.packets[i] instanceof openpgp_packet_secret_subkey || + this.packets[i] instanceof openpgp_packet_public_subkey) + subkeys.push(this.packets[i]); + + return subkeys; + } + + this.getAllKeys = function() { + return [this.getKey()].concat(this.getSubkeys()); + } + + + this.getSigningKey = function() { - var pos = position; - while (input.length > pos) { - var result = openpgp_packet.read_packet(input, pos, input.length - pos); - if (result == null) { - util.print_error("openpgp.msg.messge decrypt:\n"+'[pub/priv_key]parsing ends here @:' + pos + " l:" + len); - break; - } else { - switch (result.tagType) { - case 2: // public key revocation signature - if (result.signatureType == 32) - this.revocationSignatures[this.revocationSignatures.length] = result; - else if (result.signatureType > 15 && result.signatureType < 20) { - if (this.certificationsignatures == null) - this.certificationSignatures = new Array(); - this.certificationSignatures[this.certificationSignatures.length] = result; - } else - util.print_error("openpgp.msg.messge decrypt:\n"+"unknown signature type directly on key "+result.signatureType+" @"+pos); - pos += result.packetLength + result.headerLength; - break; - case 7: // PrivateSubkey Packet - this.subKeys[this.subKeys.length] = result; - pos += result.packetLength + result.headerLength; - pos += result.read_nodes(this.privateKeyPacket,input, pos, input.length - pos); - break; - case 17: // User Attribute Packet - this.userAttributes[this.userAttributes.length] = result; - pos += result.packetLength + result.headerLength; - pos += result.read_nodes(this.privateKeyPacket,input, pos, input.length - pos); - break; - case 13: // User ID Packet - this.userIds[this.userIds.length] = result; - pos += result.packetLength + result.headerLength; - pos += result.read_nodes(this.privateKeyPacket, input, pos, input.length - pos); - break; - default: - this.position = position - this.privateKeyPacket.packetLength - this.privateKeyPacket.headerLength; - this.len = pos - position; - return this.len; - } - } - } - this.position = position - this.privateKeyPacket.packetLength - this.privateKeyPacket.headerLength; - this.len = pos - position; - - return this.len; - } - - function getKeyId() { - return this.privateKeyPacket.publicKey.getKeyId(); - } - - - function getSubKeyIds() { - if (this.privateKeyPacket.publicKey.version == 4) // V3 keys MUST NOT have subkeys. - var result = new Array(); - for (var i = 0; i < this.subKeys.length; i++) { - result[i] = str_sha1(this.subKeys[i].publicKey.header+this.subKeys[i].publicKey.data).substring(12,20); - } - return result; - } - - - function getSigningKey() { - if ((this.privateKeyPacket.publicKey.publicKeyAlgorithm == 17 || - this.privateKeyPacket.publicKey.publicKeyAlgorithm != 2) - && this.privateKeyPacket.publicKey.verifyKey() == 3) - return this.privateKeyPacket; - else if (this.privateKeyPacket.publicKey.version == 4) // V3 keys MUST NOT have subkeys. - for (var j = 0; j < this.privateKeyPacket.subKeys.length; j++) { - if ((this.privateKeyPacket.subKeys[j].publicKey.publicKeyAlgorithm == 17 || - this.privateKeyPacket.subKeys[j].publicKey.publicKeyAlgorithm != 2) && - this.privateKeyPacket.subKeys[j].publicKey.verifyKey() == 3) - return this.privateKeyPacket.subKeys[j]; - } + var signing = ['rsa_encrypt_sign', 'rsa_sign', 'dsa']; + signing = signing.map(function(s) { return openpgp.publickey[s]; }) + + var keys = this.getAllKeys(); + + for(var i in keys) + if(signing.indexOf(keys[i].public_algorithm) != -1) + return keys[i]; + return null; } @@ -128,66 +85,21 @@ function openpgp_msg_privatekey() { } - function decryptSecretMPIs(str_passphrase) { - return this.privateKeyPacket.decryptSecretMPIs(str_passphrase); + this.decrypt = function(passphrase) { + var keys = this.getAllKeys(); + + for(var i in keys) + if(keys[i] instanceof openpgp_packet_secret_subkey || + keys[i] instanceof openpgp_packet_secret_key) + + keys[i].decrypt(passphrase); } - function getFingerprint() { - return this.privateKeyPacket.publicKey.getFingerprint(); - } // TODO need to implement this function revoke() { } - /** - * extracts the public key part - * @return {String} OpenPGP armored text containing the public key - * returns null if no sufficient data to extract public key - */ - function extractPublicKey() { - // add public key - var key = this.privateKeyPacket.publicKey.header + this.privateKeyPacket.publicKey.data; - for (var i = 0; i < this.userIds.length; i++) { - // verify userids - if (this.userIds[i].certificationSignatures.length === 0) { - util.print_error("extractPublicKey - missing certification signatures"); - return null; - } - var userIdPacket = new openpgp_packet_userid(); - // add userids - key += userIdPacket.write_packet(this.userIds[i].text); - for (var j = 0; j < this.userIds[i].certificationSignatures.length; j++) { - var certSig = this.userIds[i].certificationSignatures[j]; - // add signatures - key += openpgp_packet.write_packet_header(2, certSig.data.length) + certSig.data; - } - } - for (var k = 0; k < this.subKeys.length; k++) { - var pubSubKey = this.subKeys[k].publicKey; - // add public subkey package - key += openpgp_packet.write_old_packet_header(14, pubSubKey.data.length) + pubSubKey.data; - var subKeySig = this.subKeys[k].subKeySignature; - if (subKeySig !== null) { - // add subkey signature - key += openpgp_packet.write_packet_header(2, subKeySig.data.length) + subKeySig.data; - } else { - util.print_error("extractPublicKey - missing subkey signature"); - return null; - } - } - var publicArmored = openpgp_encoding_armor(4, key); - return publicArmored; - } - - this.extractPublicKey = extractPublicKey; - this.getSigningKey = getSigningKey; - this.getFingerprint = getFingerprint; - this.getPreferredSignatureHashAlgorithm = getPreferredSignatureHashAlgorithm; - this.read_nodes = read_nodes; - this.decryptSecretMPIs = decryptSecretMPIs; - this.getSubKeyIds = getSubKeyIds; - this.getKeyId = getKeyId; } diff --git a/src/packet/public_key.js b/src/packet/public_key.js index 846bd4e4..a4878af3 100644 --- a/src/packet/public_key.js +++ b/src/packet/public_key.js @@ -26,37 +26,10 @@ */ function openpgp_packet_public_key() { this.tag = 6; - this.version = 4; this.created = new Date(); this.mpi = []; this.algorithm = openpgp.publickey.rsa_sign; - - var public_mpis = function(algorithm) { - // - A series of multiprecision integers comprising the key material: - // Algorithm-Specific Fields for RSA public keys: - // - a multiprecision integer (MPI) of RSA public modulus n; - // - an MPI of RSA public encryption exponent e. - if (algorithm > 0 && algorithm < 4) - return 2; - // Algorithm-Specific Fields for Elgamal public keys: - // - MPI of Elgamal prime p; - // - MPI of Elgamal group generator g; - // - MPI of Elgamal public key value y (= g**x mod p where x is secret). - else if (algorithm == 16) - return 3; - - // Algorithm-Specific Fields for DSA public keys: - // - MPI of DSA prime p; - // - MPI of DSA group order q (q is a prime divisor of p-1); - // - MPI of DSA group generator g; - // - MPI of DSA public-key value y (= g**x mod p where x is secret). - else if (algorithm == 17) - return 4; - else - return 0; - }; - /** * Internal Parser for public keys as specified in RFC 4880 section @@ -67,11 +40,11 @@ function openpgp_packet_public_key() { * @param {Integer} len Length of the packet or remaining length of input * @return {Object} This object with attributes set by the parser */ - this.read = function(bytes) { + this.readPublicKey = this.read = function(bytes) { // A one-octet version number (3 or 4). - this.version = bytes[0].charCodeAt(); + var version = bytes[0].charCodeAt(); - if (this.version == 3) { + if (version == 3) { /* // A four-octet number denoting the time that the key was created. this.creationTime = new Date(((input[mypos++].charCodeAt() << 24) | @@ -120,14 +93,14 @@ function openpgp_packet_public_key() { } this.packetLength = mypos-position; */ - } else if (this.version == 4) { + } else if (version == 4) { // - A four-octet number denoting the time that the key was created. this.created = openpgp_packet_time_read(bytes.substr(1, 4)); // - A one-octet number denoting the public-key algorithm of this key. this.algorithm = bytes[5].charCodeAt(); - var mpicount = public_mpis(this.algorithm); + var mpicount = openpgp_crypto_getPublicMpiCount(this.algorithm); this.mpi = []; var bmpi = bytes.substr(6); @@ -162,12 +135,15 @@ function openpgp_packet_public_key() { * @return {Object} {body: [string]OpenPGP packet body contents, * header: [string] OpenPGP packet header, string: [string] header+body} */ - this.write = function() { + this.writePublicKey = this.write = function() { + // Version var result = String.fromCharCode(4); result += openpgp_packet_time_write(this.created); result += String.fromCharCode(this.algorithm); - for(var i in this.mpi) { + var mpicount = openpgp_crypto_getPublicMpiCount(this.algorithm); + + for(var i = 0; i < mpicount; i++) { result += this.mpi[i].write(); } diff --git a/src/packet/public_key_encrypted_session_key.js b/src/packet/public_key_encrypted_session_key.js index 37e6448e..491dbad2 100644 --- a/src/packet/public_key_encrypted_session_key.js +++ b/src/packet/public_key_encrypted_session_key.js @@ -122,7 +122,7 @@ function openpgp_packet_public_key_encrypted_session_key() { return result; } - this.encrypt = function(public_key_mpi) { + this.encrypt = function(key) { var data = String.fromCharCode(this.symmetric_algorithm); data += this.symmetric_key; @@ -133,11 +133,11 @@ function openpgp_packet_public_key_encrypted_session_key() { var mpi = new openpgp_type_mpi(); mpi.fromBytes(openpgp_encoding_eme_pkcs1_encode( data, - public_key_mpi[0].byteLength())); + key.mpi[0].byteLength())); this.encrypted = openpgp_crypto_asymetricEncrypt( this.public_key_algorithm, - public_key_mpi, + key.mpi, mpi); } @@ -151,11 +151,10 @@ function openpgp_packet_public_key_encrypted_session_key() { * Private key with secMPIs unlocked * @return {String} The unencrypted session key */ - this.decrypt = function(public_key_mpi, private_key_mpi) { + this.decrypt = function(key) { var result = openpgp_crypto_asymetricDecrypt( this.public_key_algorithm, - public_key_mpi, - private_key_mpi, + key.mpi, this.encrypted).toBytes(); var checksum = ((result.charCodeAt(result.length - 2) << 8) @@ -163,7 +162,7 @@ function openpgp_packet_public_key_encrypted_session_key() { var decoded = openpgp_encoding_eme_pkcs1_decode( result, - public_key_mpi[0].byteLength()); + key.mpi[0].byteLength()); var key = decoded.substring(1, decoded.length - 2); diff --git a/src/packet/secret_key.js b/src/packet/secret_key.js index a4bab5a2..2b90c50c 100644 --- a/src/packet/secret_key.js +++ b/src/packet/secret_key.js @@ -25,9 +25,9 @@ * major versions. Consequently, this section is complex. */ function openpgp_packet_secret_key() { + openpgp_packet_public_key.call(this); + this.tag = 5; - this.public_key = new openpgp_packet_public_key(); - this.mpi = []; this.encrypted = null; @@ -74,7 +74,9 @@ function openpgp_packet_secret_key() { function write_cleartext_mpi(hash_algorithm, mpi) { var bytes= ''; - for(var i in mpi) { + var discard = openpgp_crypto_getPublicMpiCount(this.algorithm); + + for(var i = discard; i < mpi.length; i++) { bytes += mpi[i].write(); } @@ -96,7 +98,7 @@ function openpgp_packet_secret_key() { */ this.read = function(bytes) { // - A Public-Key or Public-Subkey packet, as described above. - var len = this.public_key.read(bytes); + var len = this.readPublicKey(bytes); bytes = bytes.substr(len); @@ -115,8 +117,8 @@ function openpgp_packet_secret_key() { // key data. These algorithm-specific fields are as described // below. - this.mpi = parse_cleartext_mpi('mod', bytes.substr(1), - this.public_key.algorithm); + this.mpi = this.mpi.concat(parse_cleartext_mpi('mod', bytes.substr(1), + this.algorithm)); } } @@ -135,7 +137,7 @@ function openpgp_packet_secret_key() { header: [string] OpenPGP packet header, string: [string] header+body} */ this.write = function() { - var bytes = this.public_key.write(); + var bytes = this.writePublicKey(); if(!this.encrypted) { bytes += String.fromCharCode(0); @@ -297,8 +299,8 @@ function openpgp_packet_secret_key() { hash = 'mod'; - this.mpi = parse_cleartext_mpi(hash, cleartext, - this.public_key.algorithm); + this.mpi = this.mpi.concat(parse_cleartext_mpi(hash, cleartext, + this.algorithm)); } /** @@ -332,10 +334,11 @@ function openpgp_packet_secret_key() { } } +openpgp_packet_secret_key.prototype = new openpgp_packet_public_key(); + function openpgp_packet_secret_subkey() { openpgp_packet_secret_key.call(this); this.tag = 7; } - diff --git a/src/packet/signature.js b/src/packet/signature.js index 6c6e78a7..572e996a 100644 --- a/src/packet/signature.js +++ b/src/packet/signature.js @@ -30,7 +30,6 @@ function openpgp_packet_signature() { this.signatureType = null; this.hashAlgorithm = null; this.publicKeyAlgorithm = null; - this.version = 4; this.signatureData = null; this.signedHashValue = null; @@ -80,9 +79,9 @@ function openpgp_packet_signature() { this.read = function(bytes) { var i = 0; - this.version = bytes[i++].charCodeAt(); + var version = bytes[i++].charCodeAt(); // switch on version (3 and 4) - switch (this.version) { + switch (version) { case 3: // One-octet length of following hashed material. MUST be 5. if (bytes[i++].charCodeAt() != 5) @@ -181,8 +180,7 @@ function openpgp_packet_signature() { * @param {Object} data Contains packets to be signed. * @param {openpgp_msg_privatekey} privatekey private key used to sign the message. */ - this.sign = function(privatekey, data) { - var publickey = privatekey.public_key; + this.sign = function(key, data) { var result = String.fromCharCode(4); result += String.fromCharCode(this.signatureType); @@ -198,14 +196,15 @@ function openpgp_packet_signature() { var trailer = this.calculateTrailer(); - var toHash = this.toSign(this.signatureType, data) + this.signatureData + trailer; + var toHash = this.toSign(this.signatureType, data) + + this.signatureData + trailer; var hash = openpgp_crypto_hashData(this.hashAlgorithm, toHash); this.signedHashValue = hash.substr(0, 2); - this.signature = openpgp_crypto_signData(this.hashAlgorithm, this.publicKeyAlgorithm, - publickey.mpi, privatekey.mpi, toHash); + this.signature = openpgp_crypto_signData(this.hashAlgorithm, + this.publicKeyAlgorithm, key.mpi, toHash); } /** @@ -360,6 +359,7 @@ function openpgp_packet_signature() { } }; + // Produces data to produce signature on this.toSign = function(type, data) { var t = openpgp_packet_signature.type; @@ -413,7 +413,7 @@ function openpgp_packet_signature() { if(data.key == undefined) throw new Error('Key packet is required for this sigtature.'); - var bytes = data.key.write(); + var bytes = data.key.writePublicKey(); return String.fromCharCode(0x99) + openpgp_packet_number_write(bytes.length, 2) + @@ -436,7 +436,7 @@ function openpgp_packet_signature() { this.calculateTrailer = function() { // calculating the trailer var trailer = ''; - trailer += String.fromCharCode(this.version); + trailer += String.fromCharCode(4); // Version trailer += String.fromCharCode(0xFF); trailer += openpgp_packet_number_write(this.signatureData.length, 4); return trailer diff --git a/test/general/packet.js b/test/general/packet.js index 12eaf1ac..fd2df68b 100644 --- a/test/general/packet.js +++ b/test/general/packet.js @@ -119,25 +119,16 @@ unittests.register("Packet testing", function() { }, function() { var rsa = new RSA(), - key = rsa.generate(512, "10001") + mpi = rsa.generate(512, "10001") + var mpi = [mpi.n, mpi.ee, mpi.d, mpi.p, mpi.q, mpi.u]; - var key = [ - [key.d, key.p, key.q, key.u], - [key.n, key.ee]]; - - key = key.map(function(k) { - return k.map(function(bn) { + mpi = mpi.map(function(k) { var mpi = new openpgp_type_mpi(); - mpi.fromBigInteger(bn); + mpi.fromBigInteger(k); return mpi; - }); }); - var mpi = new openpgp_type_mpi(); - mpi.fromBigInteger(key[0][1].data); - mpi.read(mpi.write()); - var enc = new openpgp_packet_public_key_encrypted_session_key(), msg = new openpgp_packetlist(), msg2 = new openpgp_packetlist(); @@ -146,13 +137,13 @@ unittests.register("Packet testing", function() { enc.public_key_algorithm = openpgp.publickey.rsa_encrypt; enc.symmetric_algorithm = openpgp.symmetric.aes256; enc.public_key_id.bytes = '12345678'; - enc.encrypt(key[1]); + enc.encrypt({ mpi: mpi }); msg.push(enc); msg2.read(msg.write()); - msg2[0].decrypt(key[1], key[0]); + msg2[0].decrypt({ mpi: mpi }); return new test_result('Public key encrypted symmetric key packet', msg2[0].symmetric_key == enc.symmetric_key && @@ -192,9 +183,9 @@ unittests.register("Packet testing", function() { enc.symmetric_algorithm = openpgp.symmetric.aes256; enc.public_key_id.bytes = '12345678'; - enc.encrypt(key.public_key.mpi); + enc.encrypt(key); - enc.decrypt(key.public_key.mpi, key.mpi); + enc.decrypt(key); return new test_result('Secret key packet (reading, unencrpted)', enc.symmetric_key == secret); @@ -255,7 +246,7 @@ unittests.register("Packet testing", function() { var msg = new openpgp_packetlist(); msg.read(openpgp_encoding_dearmor(armored_msg).openpgp); - msg[0].decrypt(key.public_key.mpi, key.mpi); + msg[0].decrypt(key); msg[1].decrypt(msg[0].symmetric_algorithm, msg[0].symmetric_key); var text = msg[1].packets[0].packets[0].data; @@ -317,7 +308,7 @@ unittests.register("Packet testing", function() { var msg = new openpgp_packetlist(); msg.read(openpgp_encoding_dearmor(armored_msg).openpgp); - msg[0].decrypt(key.public_key.mpi, key.mpi); + msg[0].decrypt(key); msg[1].decrypt(msg[0].symmetric_algorithm, msg[0].symmetric_key); var text = msg[1].packets[0].packets[0].data; @@ -333,16 +324,16 @@ unittests.register("Packet testing", function() { key.read(openpgp_encoding_dearmor(armored_key).openpgp); - var verified = key[2].verify(key[0].public_key, + var verified = key[2].verify(key[0], { userid: key[1], - key: key[0].public_key + key: key[0] }); - verified = verified && key[4].verify(key[0].public_key, + verified = verified && key[4].verify(key[0], { - key: key[0].public_key, - bind: key[3].public_key + key: key[0], + bind: key[3], }) @@ -374,14 +365,14 @@ unittests.register("Packet testing", function() { msg.read(openpgp_encoding_dearmor(armored_msg).openpgp); - msg[0].decrypt(key[3].public_key.mpi, key[3].mpi); + msg[0].decrypt(key[3]); msg[1].decrypt(msg[0].symmetric_algorithm, msg[0].symmetric_key); var payload = msg[1].packets[0].packets - var verified = payload[2].verify(key[0].public_key, + var verified = payload[2].verify(key[0], { literal: payload[1] }); @@ -398,20 +389,15 @@ unittests.register("Packet testing", function() { mpi = rsa.generate(512, "10001") - var mpi = [ - [mpi.d, mpi.p, mpi.q, mpi.u], - [mpi.n, mpi.ee]]; + var mpi = [mpi.n, mpi.ee, mpi.d, mpi.p, mpi.q, mpi.u]; mpi = mpi.map(function(k) { - return k.map(function(bn) { var mpi = new openpgp_type_mpi(); - mpi.fromBigInteger(bn); + mpi.fromBigInteger(k); return mpi; - }); }); - key[0].public_key.mpi = mpi[1]; - key[0].mpi = mpi[0]; + key[0].mpi = mpi; key[0].encrypt('hello'); @@ -430,21 +416,15 @@ unittests.register("Packet testing", function() { var rsa = new RSA(), mpi = rsa.generate(512, "10001") - - var mpi = [ - [mpi.d, mpi.p, mpi.q, mpi.u], - [mpi.n, mpi.ee]]; + var mpi = [mpi.n, mpi.ee, mpi.d, mpi.p, mpi.q, mpi.u]; mpi = mpi.map(function(k) { - return k.map(function(bn) { var mpi = new openpgp_type_mpi(); - mpi.fromBigInteger(bn); + mpi.fromBigInteger(k); return mpi; - }); }); - key.public_key.mpi = mpi[1]; - key.mpi = mpi[0]; + key.mpi = mpi; var signed = new openpgp_packetlist(), literal = new openpgp_packet_literal(), @@ -466,7 +446,7 @@ unittests.register("Packet testing", function() { var signed2 = new openpgp_packetlist(); signed2.read(raw); - var verified = signed2[1].verify(key.public_key, { literal: signed2[0] }); + var verified = signed2[1].verify(key, { literal: signed2[0] }); return new test_result('Writing and verification of a signature packet.',