diff --git a/src/js/crypto/pgp.js b/src/js/crypto/pgp.js index af20886..074b49d 100644 --- a/src/js/crypto/pgp.js +++ b/src/js/crypto/pgp.js @@ -177,10 +177,15 @@ define(function(require) { * Decrypt and verify a pgp message for a single sender */ PGP.prototype.decrypt = function(ciphertext, senderKey, callback) { - var privateKey, msg, keymat, sesskey, decrypted; + var privateKey, publicKey, pubKeys, msg, keymat, sesskey, decrypted; privateKey = openpgp.keyring.exportPrivateKey(0).obj; - senderKey = openpgp.read_publicKey(senderKey)[0]; + publicKey = openpgp.read_publicKey(senderKey)[0]; + pubKeys = [{ + armored: senderKey, + obj: publicKey, + keyId: publicKey.getKeyId() + }]; try { msg = openpgp.read_message(ciphertext)[0]; @@ -223,15 +228,26 @@ define(function(require) { // decrypt and verify ciphertext try { - decrypted = msg.decryptAndVerifySignature(keymat, sesskey, senderKey); + decrypted = msg.decryptAndVerifySignature(keymat, sesskey, pubKeys); } catch (err) { callback({ - errMsg: 'Error reading PGP message!', + errMsg: 'Error decrypting PGP message!', err: err }); return; } + // check if signatures are ok + for (var k = 0; k < decrypted.validSignatures.length; k++) { + if (!decrypted.validSignatures[k]) { + callback({ + errMsg: 'Error verifying PGP signature!' + }); + return; + } + } + + // return decrypted plaintext callback(null, decrypted.text); }; diff --git a/src/lib/openpgp/openpgp.js b/src/lib/openpgp/openpgp.js index dc721f1..40f0274 100644 --- a/src/lib/openpgp/openpgp.js +++ b/src/lib/openpgp/openpgp.js @@ -3834,6 +3834,8 @@ function openpgp_crypto_verifySignature(algo, hash_algo, msg_MPIs, publickey_MPI util.print_error("PKCS1 padding in message or key incorrect. Aborting..."); return false; } + util.print_debug('hash: '+util.hexdump(hash)); + util.print_debug('calc_hash: '+util.hexdump(calc_hash)); return hash == calc_hash; case 16: // Elgamal (Encrypt-Only) [ELGAMAL] [HAC] @@ -4038,7 +4040,6 @@ function openpgp_crypto_getRandomBigIntegerInRange(min, max) { //This is a test method to ensure that encryption/decryption with a given 1024bit RSAKey object functions as intended function openpgp_crypto_testRSA(key){ - debugger; var rsa = new RSA(); var mpi = new openpgp_type_mpi(); mpi.create(openpgp_encoding_eme_pkcs1_encode('ABABABAB', 128)); @@ -6485,9 +6486,6 @@ JXG.Util.Unzip = function (barray){ X = currentTree[xtreepos]; } } - if (debug) - document.write("ret3"); - return -1; }; function DeflateLoop() { @@ -6929,7 +6927,7 @@ JXG.Util.Unzip.prototype.unzip = function() { CRC = 0xffffffff; SIZE = 0; - if (size = 0 && fileOut.charAt(fileout.length-1)=="/"){ + if (size == 0 && fileOut.charAt(fileout.length-1)=="/"){ //skipdir if (debug) alert("skipdir"); @@ -7424,7 +7422,7 @@ function openpgp_config() { keyserver: "keyserver.linux.it" // "pgp.mit.edu:11371" }; - this.versionstring ="OpenPGP.js v.1.20131116"; + this.versionstring ="OpenPGP.js v.1.20131205"; this.commentstring ="Whiteout Mail - http://whiteout.io"; /** * Reads the config out of the HTML5 local storage @@ -7565,77 +7563,107 @@ function r2s(t) { * and an attribute "openpgp" containing the bytes. */ function openpgp_encoding_deArmor(text) { + var reSplit = /^-----[^-]+-----$\n/m; + text = text.replace(/\r/g, ''); - // remove whitespace of blank line to allow later split at \n\n - text = text.replace(/\n\s+\n/, '\n\n'); var type = openpgp_encoding_get_type(text); + var splittedtext = text.split(reSplit); + + // IE has a bug in split with a re. If the pattern matches the beginning of the + // string it doesn't create an empty array element 0. So we need to detect this + // so we know the index of the data we are interested in. + var indexBase = 1; + + var result, checksum; + + if (text.search(reSplit) != splittedtext[0].length) { + indexBase = 0; + } + if (type != 2) { - var splittedtext = text.split('-----'); - // splittedtext[0] - should be the empty string - // splittedtext[1] - should be BEGIN... - // splittedtext[2] - the message and checksum - // splittedtest[3] - should be END... + // splittedtext[indexBase] - the message and checksum // chunks separated by blank lines - var splittedChunks = splittedtext[2] - .split('\n\n'); - var messageAndChecksum = splittedtext[2] - .split('\n\n')[1] - .split('\n='); + var msg = openpgp_encoding_split_headers(splittedtext[indexBase].replace(/^- /mg, '')); + var msg_sum = openpgp_encoding_split_checksum(msg.body); - var message = messageAndChecksum[0] - .replace(/\n- /g,"\n"); - // sometimes, there's a blank line between message and checksum - var checksum; - if(messageAndChecksum.length == 1){ - // blank line - checksum = splittedtext[2] - .replace(/[\n=]/g, ""); - } else { - // no blank line - checksum = messageAndChecksum[1] - .split('\n')[0]; - } - - var data = { - openpgp: openpgp_encoding_base64_decode(message), + result = { + openpgp: openpgp_encoding_base64_decode(msg_sum.body), type: type }; - - if (verifyCheckSum(data.openpgp, checksum)) { - return data; - } else { - util.print_error("Ascii armor integrity check on message failed: '" - + checksum - + "' should be '" - + getCheckSum(data)) + "'"; - return false; - } + checksum = msg_sum.checksum; } else { - var splittedtext = text.split('-----'); + // splittedtext[indexBase] - the message + // splittedtext[indexBase + 1] - the signature and checksum - var result = { - text: splittedtext[2] - .replace(/\n- /g,"\n") - .split("\n\n")[1], - openpgp: openpgp_encoding_base64_decode(splittedtext[4] - .split("\n\n")[1] - .split("\n=")[0]), + var msg = openpgp_encoding_split_headers(splittedtext[indexBase].replace(/^- /mg, '')); + var sig = openpgp_encoding_split_headers(splittedtext[indexBase + 1].replace(/^- /mg, '')); + var sig_sum = openpgp_encoding_split_checksum(sig.body); + + result = { + text: msg.body.replace(/\n$/, "").replace(/\n/g, "\r\n"), + openpgp: openpgp_encoding_base64_decode(sig_sum.body), type: type }; - if (verifyCheckSum(result.openpgp, splittedtext[4] - .split("\n\n")[1] - .split("\n=")[1])) - - return result; - else { - util.print_error("Ascii armor integrity check on message failed"); - return false; - } + checksum = sig_sum.checksum; } + + if (!verifyCheckSum(result.openpgp, checksum)) { + util.print_error("Ascii armor integrity check on message failed: '" + + checksum + + "' should be '" + + getCheckSum(result) + "'"); + return false; + } else { + return result; + } +} + +/** + * Splits a message into two parts, the headers and the body. This is an internal function + * @param {String} text OpenPGP armored message part + * @returns {(Boolean|Object)} Either false in case of an error + * or an object with attribute "headers" containing the headers and + * and an attribute "body" containing the body. + */ +function openpgp_encoding_split_headers(text) { + var reEmptyLine = /^[\t ]*\n/m; + var headers = ""; + var body = text; + + var matchResult = reEmptyLine.exec(text); + + if (matchResult != null) { + headers = text.slice(0, matchResult.index); + body = text.slice(matchResult.index + matchResult[0].length); + } + + return { headers: headers, body: body }; +} + +/** + * Splits a message into two parts, the body and the checksum. This is an internal function + * @param {String} text OpenPGP armored message part + * @returns {(Boolean|Object)} Either false in case of an error + * or an object with attribute "body" containing the body + * and an attribute "checksum" containing the checksum. + */ +function openpgp_encoding_split_checksum(text) { + var reChecksumStart = /^=/m; + var body = text; + var checksum = ""; + + var matchResult = reChecksumStart.exec(text); + + if (matchResult != null) { + body = text.slice(0, matchResult.index); + checksum = text.slice(matchResult.index + 1); + } + + return { body: body, checksum: checksum }; } /** @@ -7650,44 +7678,46 @@ function openpgp_encoding_deArmor(text) { * null = unknown */ function openpgp_encoding_get_type(text) { - var splittedtext = text.split('-----'); + var reHeader = /^-----([^-]+)-----$\n/m; + + var header = text.match(reHeader); // BEGIN PGP MESSAGE, PART X/Y // Used for multi-part messages, where the armor is split amongst Y // parts, and this is the Xth part out of Y. - if (splittedtext[1].match(/BEGIN PGP MESSAGE, PART \d+\/\d+/)) { + if (header[1].match(/BEGIN PGP MESSAGE, PART \d+\/\d+/)) { return 0; } else // BEGIN PGP MESSAGE, PART X // Used for multi-part messages, where this is the Xth part of an // unspecified number of parts. Requires the MESSAGE-ID Armor // Header to be used. - if (splittedtext[1].match(/BEGIN PGP MESSAGE, PART \d+/)) { + if (header[1].match(/BEGIN PGP MESSAGE, PART \d+/)) { return 1; } else - // BEGIN PGP SIGNATURE + // BEGIN PGP SIGNED MESSAGE // Used for detached signatures, OpenPGP/MIME signatures, and // cleartext signatures. Note that PGP 2.x uses BEGIN PGP MESSAGE // for detached signatures. - if (splittedtext[1].match(/BEGIN PGP SIGNED MESSAGE/)) { + if (header[1].match(/BEGIN PGP SIGNED MESSAGE/)) { return 2; } else // BEGIN PGP MESSAGE // Used for signed, encrypted, or compressed files. - if (splittedtext[1].match(/BEGIN PGP MESSAGE/)) { + if (header[1].match(/BEGIN PGP MESSAGE/)) { return 3; } else // BEGIN PGP PUBLIC KEY BLOCK // Used for armoring public keys. - if (splittedtext[1].match(/BEGIN PGP PUBLIC KEY BLOCK/)) { + if (header[1].match(/BEGIN PGP PUBLIC KEY BLOCK/)) { return 4; } else // BEGIN PGP PRIVATE KEY BLOCK // Used for armoring private keys. - if (splittedtext[1].match(/BEGIN PGP PRIVATE KEY BLOCK/)) { + if (header[1].match(/BEGIN PGP PRIVATE KEY BLOCK/)) { return 5; } } @@ -8040,14 +8070,14 @@ function _openpgp () { while (mypos != input.length) { var first_packet = openpgp_packet.read_packet(input, mypos, l); // public key parser - if (input[mypos].charCodeAt() == 0x99 || first_packet.tagType == 6) { + if (input.charCodeAt(mypos) == 0x99 || first_packet.tagType == 6) { publicKeys[publicKeyCount] = new openpgp_msg_publickey(); publicKeys[publicKeyCount].header = input.substring(mypos,mypos+3); - if (input[mypos].charCodeAt() == 0x99) { + if (input.charCodeAt(mypos) == 0x99) { // parse the length and read a tag6 packet mypos++; - var l = (input[mypos++].charCodeAt() << 8) - | input[mypos++].charCodeAt(); + var l = (input.charCodeAt(mypos++) << 8) + | input.charCodeAt(mypos++); publicKeys[publicKeyCount].publicKeyPacket = new openpgp_packet_keymaterial(); publicKeys[publicKeyCount].publicKeyPacket.header = publicKeys[publicKeyCount].header; publicKeys[publicKeyCount].publicKeyPacket.read_tag6(input, mypos, l); @@ -8110,7 +8140,7 @@ function _openpgp () { function read_message(armoredText) { var dearmored; try{ - dearmored = openpgp_encoding_deArmor(armoredText.replace(/\r/g,'')); + dearmored = openpgp_encoding_deArmor(armoredText); } catch(e){ util.print_error('no message found!'); @@ -8794,7 +8824,7 @@ function openpgp_msg_message() { function verifySignature(pubkey) { var result = false; if (this.signature.tagType == 2) { - if(!pubkey || pubkey.length == 0){ + if (!pubkey || pubkey.length == 0) { var pubkey; if (this.signature.version == 4) { pubkey = openpgp.keyring.getPublicKeysForKeyId(this.signature.issuerKeyId); @@ -8805,14 +8835,14 @@ function openpgp_msg_message() { return false; } } - if (pubkey.length == 0) + if (pubkey.length == 0) { util.print_warning("Unable to verify signature of issuer: "+util.hexstrdump(this.signature.issuerKeyId)+". Public key not found in keyring."); - else { + } else { for (var i = 0 ; i < pubkey.length; i++) { - var tohash = this.text.replace(/\r\n/g,"\n").replace(/\n/g,"\r\n"); - if (this.signature.verify(tohash, pubkey[i])) { + if (this.signature.verify(this.text, pubkey[i])) { util.print_info("Found Good Signature from "+pubkey[i].obj.userIds[0].text+" (0x"+util.hexstrdump(pubkey[i].obj.getKeyId()).substring(8)+")"); result = true; + break; } else { util.print_error("Signature verification failed: Bad Signature from "+pubkey[i].obj.userIds[0].text+" (0x"+util.hexstrdump(pubkey[0].obj.getKeyId()).substring(8)+")"); } @@ -9061,9 +9091,6 @@ function openpgp_msg_privatekey() { * @classdesc Decoded public key object for internal openpgp.js use */ function openpgp_msg_publickey() { - this.data; - this.position; - this.len; this.tostring = "OPENPGP PUBLIC KEY\n"; this.bindingSignature = null; this.publicKeyPacket = null; @@ -9621,7 +9648,7 @@ function openpgp_packet_encryptedintegrityprotecteddata() { this.packetLength = len; // - A one-octet version number. The only currently defined value is // 1. - this.version = input[position].charCodeAt(); + this.version = input.charCodeAt(position); if (this.version != 1) { util .print_error('openpgp.packet.encryptedintegrityprotecteddata.js\nunknown encrypted integrity protected data packet version: ' @@ -9779,11 +9806,11 @@ function openpgp_packet_encryptedsessionkey() { return null; } - this.version = input[mypos++].charCodeAt(); + this.version = input.charCodeAt(mypos++); this.keyId = new openpgp_type_keyid(); this.keyId.read_packet(input, mypos); mypos += 8; - this.publicKeyAlgorithmUsed = input[mypos++].charCodeAt(); + this.publicKeyAlgorithmUsed = input.charCodeAt(mypos++); switch (this.publicKeyAlgorithmUsed) { case 1: @@ -10059,7 +10086,7 @@ function _openpgp_packet() { // some sanity checks if (input == null || input.length <= position || input.substring(position).length < 2 - || (input[position].charCodeAt() & 0x80) == 0) { + || (input.charCodeAt(position) & 0x80) == 0) { util .print_error("Error during parsing. This message / key is probably not containing a valid OpenPGP format."); return null; @@ -10069,18 +10096,18 @@ function _openpgp_packet() { var format = -1; format = 0; // 0 = old format; 1 = new format - if ((input[mypos].charCodeAt() & 0x40) != 0) { + if ((input.charCodeAt(mypos) & 0x40) != 0) { format = 1; } var packet_length_type; if (format) { // new format header - tag = input[mypos].charCodeAt() & 0x3F; // bit 5-0 + tag = input.charCodeAt(mypos) & 0x3F; // bit 5-0 } else { // old format header - tag = (input[mypos].charCodeAt() & 0x3F) >> 2; // bit 5-2 - packet_length_type = input[mypos].charCodeAt() & 0x03; // bit 1-0 + tag = (input.charCodeAt(mypos) & 0x3F) >> 2; // bit 5-2 + packet_length_type = input.charCodeAt(mypos) & 0x03; // bit 1-0 } // header octet parsing done @@ -10096,19 +10123,19 @@ function _openpgp_packet() { switch (packet_length_type) { case 0: // The packet has a one-octet length. The header is 2 octets // long. - packet_length = input[mypos++].charCodeAt(); + packet_length = input.charCodeAt(mypos++); break; case 1: // The packet has a two-octet length. The header is 3 octets // long. - packet_length = (input[mypos++].charCodeAt() << 8) - | input[mypos++].charCodeAt(); + packet_length = (input.charCodeAt(mypos++) << 8) + | input.charCodeAt(mypos++); break; case 2: // The packet has a four-octet length. The header is 5 // octets long. - packet_length = (input[mypos++].charCodeAt() << 24) - | (input[mypos++].charCodeAt() << 16) - | (input[mypos++].charCodeAt() << 8) - | input[mypos++].charCodeAt(); + packet_length = (input.charCodeAt(mypos++) << 24) + | (input.charCodeAt(mypos++) << 16) + | (input.charCodeAt(mypos++) << 8) + | input.charCodeAt(mypos++); break; default: // 3 - The packet is of indeterminate length. The header is 1 @@ -10129,50 +10156,50 @@ function _openpgp_packet() { { // 4.2.2.1. One-Octet Lengths - if (input[mypos].charCodeAt() < 192) { - packet_length = input[mypos++].charCodeAt(); + if (input.charCodeAt(mypos) < 192) { + packet_length = input.charCodeAt(mypos++); util.print_debug("1 byte length:" + packet_length); // 4.2.2.2. Two-Octet Lengths - } else if (input[mypos].charCodeAt() >= 192 - && input[mypos].charCodeAt() < 224) { - packet_length = ((input[mypos++].charCodeAt() - 192) << 8) - + (input[mypos++].charCodeAt()) + 192; + } else if (input.charCodeAt(mypos) >= 192 + && input.charCodeAt(mypos) < 224) { + packet_length = ((input.charCodeAt(mypos++) - 192) << 8) + + (input.charCodeAt(mypos++)) + 192; util.print_debug("2 byte length:" + packet_length); // 4.2.2.4. Partial Body Lengths - } else if (input[mypos].charCodeAt() > 223 - && input[mypos].charCodeAt() < 255) { - packet_length = 1 << (input[mypos++].charCodeAt() & 0x1F); + } else if (input.charCodeAt(mypos) > 223 + && input.charCodeAt(mypos) < 255) { + packet_length = 1 << (input.charCodeAt(mypos++) & 0x1F); util.print_debug("4 byte length:" + packet_length); // EEEK, we're reading the full data here... var mypos2 = mypos + packet_length; bodydata = input.substring(mypos, mypos + packet_length); while (true) { - if (input[mypos2].charCodeAt() < 192) { - var tmplen = input[mypos2++].charCodeAt(); + if (input.charCodeAt(mypos2) < 192) { + var tmplen = input.charCodeAt(mypos2++); packet_length += tmplen; bodydata += input.substring(mypos2, mypos2 + tmplen); mypos2 += tmplen; break; - } else if (input[mypos2].charCodeAt() >= 192 - && input[mypos2].charCodeAt() < 224) { - var tmplen = ((input[mypos2++].charCodeAt() - 192) << 8) - + (input[mypos2++].charCodeAt()) + 192; + } else if (input.charCodeAt(mypos2) >= 192 + && input.charCodeAt(mypos2) < 224) { + var tmplen = ((input.charCodeAt(mypos2++) - 192) << 8) + + (input.charCodeAt(mypos2++)) + 192; packet_length += tmplen; bodydata += input.substring(mypos2, mypos2 + tmplen); mypos2 += tmplen; break; - } else if (input[mypos2].charCodeAt() > 223 - && input[mypos2].charCodeAt() < 255) { - var tmplen = 1 << (input[mypos2++].charCodeAt() & 0x1F); + } else if (input.charCodeAt(mypos2) > 223 + && input.charCodeAt(mypos2) < 255) { + var tmplen = 1 << (input.charCodeAt(mypos2++) & 0x1F); packet_length += tmplen; bodydata += input.substring(mypos2, mypos2 + tmplen); mypos2 += tmplen; } else { mypos2++; - var tmplen = (input[mypos2++].charCodeAt() << 24) - | (input[mypos2++].charCodeAt() << 16) - | (input[mypos2++].charCodeAt() << 8) - | input[mypos2++].charCodeAt(); + var tmplen = (input.charCodeAt(mypos2++) << 24) + | (input.charCodeAt(mypos2++) << 16) + | (input.charCodeAt(mypos2++) << 8) + | input.charCodeAt(mypos2++); bodydata += input.substring(mypos2, mypos2 + tmplen); packet_length += tmplen; mypos2 += tmplen; @@ -10183,10 +10210,10 @@ function _openpgp_packet() { // 4.2.2.3. Five-Octet Lengths } else { mypos++; - packet_length = (input[mypos++].charCodeAt() << 24) - | (input[mypos++].charCodeAt() << 16) - | (input[mypos++].charCodeAt() << 8) - | input[mypos++].charCodeAt(); + packet_length = (input.charCodeAt(mypos++) << 24) + | (input.charCodeAt(mypos++) << 16) + | (input.charCodeAt(mypos++) << 8) + | input.charCodeAt(mypos++); } } @@ -10202,7 +10229,7 @@ function _openpgp_packet() { // alert('tag type: '+this.tag+' length: '+packet_length); var version = 1; // (old format; 2= new format) - // if (input[mypos++].charCodeAt() > 15) + // if (input.charCodeAt(mypos++) > 15) // version = 2; switch (tag) { @@ -10485,20 +10512,20 @@ function openpgp_packet_keymaterial() { function read_pub_key(input, position, len) { var mypos = position; // A one-octet version number (3 or 4). - this.version = input[mypos++].charCodeAt(); + this.version = input.charCodeAt(mypos++); if (this.version == 3) { // A four-octet number denoting the time that the key was created. - this.creationTime = new Date(((input[mypos++].charCodeAt() << 24) | - (input[mypos++].charCodeAt() << 16) | - (input[mypos++].charCodeAt() << 8) | - (input[mypos++].charCodeAt()))*1000); + this.creationTime = new Date(((input.charCodeAt(mypos++) << 24) | + (input.charCodeAt(mypos++) << 16) | + (input.charCodeAt(mypos++) << 8) | + (input.charCodeAt(mypos++)))*1000); // - A two-octet number denoting the time in days that this key is // valid. If this number is zero, then it does not expire. - this.expiration = (input[mypos++].charCodeAt() << 8) & input[mypos++].charCodeAt(); + this.expiration = (input.charCodeAt(mypos++) << 8) | input.charCodeAt(mypos++); // - A one-octet number denoting the public-key algorithm of this key. - this.publicKeyAlgorithm = input[mypos++].charCodeAt(); + this.publicKeyAlgorithm = input.charCodeAt(mypos++); var mpicount = 0; // - A series of multiprecision integers comprising the key material: // Algorithm-Specific Fields for RSA public keys: @@ -10534,13 +10561,13 @@ function openpgp_packet_keymaterial() { this.packetLength = mypos-position; } else if (this.version == 4) { // - A four-octet number denoting the time that the key was created. - this.creationTime = new Date(((input[mypos++].charCodeAt() << 24) | - (input[mypos++].charCodeAt() << 16) | - (input[mypos++].charCodeAt() << 8) | - (input[mypos++].charCodeAt()))*1000); + this.creationTime = new Date(((input.charCodeAt(mypos++) << 24) | + (input.charCodeAt(mypos++) << 16) | + (input.charCodeAt(mypos++) << 8) | + (input.charCodeAt(mypos++)))*1000); // - A one-octet number denoting the public-key algorithm of this key. - this.publicKeyAlgorithm = input[mypos++].charCodeAt(); + this.publicKeyAlgorithm = input.charCodeAt(mypos++); var mpicount = 0; // - A series of multiprecision integers comprising the key material: // Algorithm-Specific Fields for RSA public keys: @@ -10596,7 +10623,7 @@ function openpgp_packet_keymaterial() { // - A Public-Key or Public-Subkey packet, as described above. this.publicKey = new openpgp_packet_keymaterial(); if (this.publicKey.read_pub_key(input,position, len) == null) { - util.print_error("openpgp.packet.keymaterial.js\n"+"Failed reading public key portion of a private key: "+input[position].charCodeAt()+" "+position+" "+len+"\n Aborting here..."); + util.print_error("openpgp.packet.keymaterial.js\n"+"Failed reading public key portion of a private key: "+input.charCodeAt(position)+" "+position+" "+len+"\n Aborting here..."); return null; } this.publicKey.header = openpgp_packet.write_old_packet_header(6,this.publicKey.packetLength); @@ -10608,7 +10635,7 @@ function openpgp_packet_keymaterial() { // 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. - this.s2kUsageConventions = input[mypos++].charCodeAt(); + this.s2kUsageConventions = input.charCodeAt(mypos++); if (this.s2kUsageConventions == 0) this.hasUnencryptedSecretKeyData = true; @@ -10616,7 +10643,7 @@ function openpgp_packet_keymaterial() { // - [Optional] If string-to-key usage octet was 255 or 254, a one- // octet symmetric encryption algorithm. if (this.s2kUsageConventions == 255 || this.s2kUsageConventions == 254) { - this.symmetricEncryptionAlgorithm = input[mypos++].charCodeAt(); + this.symmetricEncryptionAlgorithm = input.charCodeAt(mypos++); } // - [Optional] If string-to-key usage octet was 255 or 254, a @@ -10714,8 +10741,8 @@ function openpgp_packet_keymaterial() { } // checksum because s2k usage convention is 0 this.checksum = new Array(); - this.checksum[0] = input[mypos++].charCodeAt(); - this.checksum[1] = input[mypos++].charCodeAt(); + this.checksum[0] = input.charCodeAt(mypos++); + this.checksum[1] = input.charCodeAt(mypos++); } return this; } @@ -11375,9 +11402,9 @@ function openpgp_packet_marker() { */ function read_packet(input, position, len) { this.packetLength = 3; - if (input[position].charCodeAt() == 0x50 && // P - input[position + 1].charCodeAt() == 0x47 && // G - input[position + 2].charCodeAt() == 0x50) // P + if (input.charCodeAt(position) == 0x50 && // P + input.charCodeAt(position + 1) == 0x47 && // G + input.charCodeAt(position + 2) == 0x50) // P return this; // marker packet does not contain "PGP" return null; @@ -11681,37 +11708,37 @@ function openpgp_packet_signature() { var mypos = position; this.packetLength = len; // alert('starting parsing signature: '+position+' '+this.packetLength); - this.version = input[mypos++].charCodeAt(); + this.version = input.charCodeAt(mypos++); // switch on version (3 and 4) switch (this.version) { case 3: // One-octet length of following hashed material. MUST be 5. - if (input[mypos++].charCodeAt() != 5) + if (input.charCodeAt(mypos++) != 5) util.print_debug("openpgp.packet.signature.js\n"+'invalid One-octet length of following hashed material. MUST be 5. @:'+(mypos-1)); var sigpos = mypos; // One-octet signature type. - this.signatureType = input[mypos++].charCodeAt(); + this.signatureType = input.charCodeAt(mypos++); // Four-octet creation time. - this.creationTime = new Date(((input[mypos++].charCodeAt()) << 24 | - (input[mypos++].charCodeAt() << 16) | (input[mypos++].charCodeAt() << 8) | - input[mypos++].charCodeAt())* 1000); + this.creationTime = new Date(((input.charCodeAt(mypos++)) << 24 | + (input.charCodeAt(mypos++) << 16) | (input.charCodeAt(mypos++) << 8) | + input.charCodeAt(mypos++))* 1000); // storing data appended to data which gets verified - this.signatureData = input.substring(position, mypos); + this.signatureData = input.substring(sigpos, mypos); // Eight-octet Key ID of signer. this.keyId = input.substring(mypos, mypos +8); mypos += 8; // One-octet public-key algorithm. - this.publicKeyAlgorithm = input[mypos++].charCodeAt(); + this.publicKeyAlgorithm = input.charCodeAt(mypos++); // One-octet hash algorithm. - this.hashAlgorithm = input[mypos++].charCodeAt(); + this.hashAlgorithm = input.charCodeAt(mypos++); // Two-octet field holding left 16 bits of signed hash value. - this.signedHashValue = (input[mypos++].charCodeAt() << 8) | input[mypos++].charCodeAt(); + this.signedHashValue = (input.charCodeAt(mypos++) << 8) | input.charCodeAt(mypos++); var mpicount = 0; // Algorithm-Specific Fields for RSA signatures: // - multiprecision integer (MPI) of RSA signature value m**d mod n. @@ -11735,13 +11762,13 @@ function openpgp_packet_signature() { } break; case 4: - this.signatureType = input[mypos++].charCodeAt(); - this.publicKeyAlgorithm = input[mypos++].charCodeAt(); - this.hashAlgorithm = input[mypos++].charCodeAt(); + this.signatureType = input.charCodeAt(mypos++); + this.publicKeyAlgorithm = input.charCodeAt(mypos++); + this.hashAlgorithm = input.charCodeAt(mypos++); // Two-octet scalar octet count for following hashed subpacket // data. - var hashed_subpacket_count = (input[mypos++].charCodeAt() << 8) + input[mypos++].charCodeAt(); + var hashed_subpacket_count = (input.charCodeAt(mypos++) << 8) + input.charCodeAt(mypos++); // Hashed subpacket data set (zero or more subpackets) var subpacket_length = 0; @@ -11761,7 +11788,7 @@ function openpgp_packet_signature() { // alert("signatureData: "+util.hexstrdump(this.signatureData)); // Two-octet scalar octet count for the following unhashed subpacket - var subpacket_count = (input[mypos++].charCodeAt() << 8) + input[mypos++].charCodeAt(); + var subpacket_count = (input.charCodeAt(mypos++) << 8) + input.charCodeAt(mypos++); // Unhashed subpacket data set (zero or more subpackets). subpacket_length = 0; @@ -11777,7 +11804,7 @@ function openpgp_packet_signature() { mypos += subpacket_count; // Two-octet field holding the left 16 bits of the signed hash // value. - this.signedHashValue = (input[mypos++].charCodeAt() << 8) | input[mypos++].charCodeAt(); + this.signedHashValue = (input.charCodeAt(mypos++) << 8) | input.charCodeAt(mypos++); // One or more multiprecision integers comprising the signature. // This portion is algorithm specific, as described above. var mpicount = 0; @@ -11873,39 +11900,39 @@ function openpgp_packet_signature() { var mypos = position; var subplen = 0; // alert('starting signature subpackage read at position:'+position+' length:'+len); - if (input[mypos].charCodeAt() < 192) { - subplen = input[mypos++].charCodeAt(); - } else if (input[mypos].charCodeAt() >= 192 && input[mypos].charCodeAt() < 224) { - subplen = ((input[mypos++].charCodeAt() - 192) << 8) + (input[mypos++].charCodeAt()) + 192; - } else if (input[mypos].charCodeAt() > 223 && input[mypos].charCodeAt() < 255) { - subplen = 1 << (input[mypos++].charCodeAt() & 0x1F); - } else if (input[mypos].charCodeAt() < 255) { + if (input.charCodeAt(mypos) < 192) { + subplen = input.charCodeAt(mypos++); + } else if (input.charCodeAt(mypos) >= 192 && input.charCodeAt(mypos) < 224) { + subplen = ((input.charCodeAt(mypos++) - 192) << 8) + (input.charCodeAt(mypos++)) + 192; + } else if (input.charCodeAt(mypos) > 223 && input.charCodeAt(mypos) < 255) { + subplen = 1 << (input.charCodeAt(mypos++) & 0x1F); + } else if (input.charCodeAt(mypos) < 255) { mypos++; - subplen = (input[mypos++].charCodeAt() << 24) | (input[mypos++].charCodeAt() << 16) - | (input[mypos++].charCodeAt() << 8) | input[mypos++].charCodeAt(); + subplen = (input.charCodeAt(mypos++) << 24) | (input.charCodeAt(mypos++) << 16) + | (input.charCodeAt(mypos++) << 8) | input.charCodeAt(mypos++); } - var type = input[mypos++].charCodeAt() & 0x7F; + var type = input.charCodeAt(mypos++) & 0x7F; // alert('signature subpacket type '+type+" with length: "+subplen); // subpacket type switch (type) { case 2: // Signature Creation Time - this.creationTime = new Date(((input[mypos++].charCodeAt() << 24) | (input[mypos++].charCodeAt() << 16) - | (input[mypos++].charCodeAt() << 8) | input[mypos++].charCodeAt())*1000); + this.creationTime = new Date(((input.charCodeAt(mypos++) << 24) | (input.charCodeAt(mypos++) << 16) + | (input.charCodeAt(mypos++) << 8) | input.charCodeAt(mypos++))*1000); break; case 3: // Signature Expiration Time - this.signatureExpirationTime = (input[mypos++].charCodeAt() << 24) - | (input[mypos++].charCodeAt() << 16) | (input[mypos++].charCodeAt() << 8) - | input[mypos++].charCodeAt(); + this.signatureExpirationTime = (input.charCodeAt(mypos++) << 24) + | (input.charCodeAt(mypos++) << 16) | (input.charCodeAt(mypos++) << 8) + | input.charCodeAt(mypos++); this.signatureNeverExpires = (this.signature_expiration_time == 0); break; case 4: // Exportable Certification - this.exportable = input[mypos++].charCodeAt() == 1; + this.exportable = input.charCodeAt(mypos++) == 1; break; case 5: // Trust Signature - this.trustLevel = input[mypos++].charCodeAt(); - this.trustAmount = input[mypos++].charCodeAt(); + this.trustLevel = input.charCodeAt(mypos++); + this.trustAmount = input.charCodeAt(mypos++); break; case 6: // Regular Expression this.regular_expression = new String(); @@ -11913,29 +11940,29 @@ function openpgp_packet_signature() { this.regular_expression += (input[mypos++]); break; case 7: // Revocable - this.revocable = input[mypos++].charCodeAt() == 1; + this.revocable = input.charCodeAt(mypos++) == 1; break; case 9: // Key Expiration Time - this.keyExpirationTime = (input[mypos++].charCodeAt() << 24) - | (input[mypos++].charCodeAt() << 16) | (input[mypos++].charCodeAt() << 8) - | input[mypos++].charCodeAt(); + this.keyExpirationTime = (input.charCodeAt(mypos++) << 24) + | (input.charCodeAt(mypos++) << 16) | (input.charCodeAt(mypos++) << 8) + | input.charCodeAt(mypos++); this.keyNeverExpires = (this.keyExpirationTime == 0); break; case 11: // Preferred Symmetric Algorithms this.preferredSymmetricAlgorithms = new Array(); for (var i = 0; i < subplen-1; i++) { - this.preferredSymmetricAlgorithms = input[mypos++].charCodeAt(); + this.preferredSymmetricAlgorithms = input.charCodeAt(mypos++); } break; case 12: // Revocation Key // (1 octet of class, 1 octet of public-key algorithm ID, 20 // octets of // fingerprint) - this.revocationKeyClass = input[mypos++].charCodeAt(); - this.revocationKeyAlgorithm = input[mypos++].charCodeAt(); + this.revocationKeyClass = input.charCodeAt(mypos++); + this.revocationKeyAlgorithm = input.charCodeAt(mypos++); this.revocationKeyFingerprint = new Array(); for ( var i = 0; i < 20; i++) { - this.revocationKeyFingerprint = input[mypos++].charCodeAt(); + this.revocationKeyFingerprint = input.charCodeAt(mypos++); } break; case 16: // Issuer @@ -11943,12 +11970,12 @@ function openpgp_packet_signature() { mypos += 8; break; case 20: // Notation Data - this.notationFlags = (input[mypos++].charCodeAt() << 24) | - (input[mypos++].charCodeAt() << 16) | - (input[mypos++].charCodeAt() << 8) | - (input[mypos++].charCodeAt()); - var nameLength = (input[mypos++].charCodeAt() << 8) | (input[mypos++].charCodeAt()); - var valueLength = (input[mypos++].charCodeAt() << 8) | (input[mypos++].charCodeAt()); + this.notationFlags = (input.charCodeAt(mypos++) << 24) | + (input.charCodeAt(mypos++) << 16) | + (input.charCodeAt(mypos++) << 8) | + (input.charCodeAt(mypos++)); + var nameLength = (input.charCodeAt(mypos++) << 8) | (input.charCodeAt(mypos++)); + var valueLength = (input.charCodeAt(mypos++) << 8) | (input.charCodeAt(mypos++)); this.notationName = ""; for (var i = 0; i < nameLength; i++) { this.notationName += input[mypos++]; @@ -11961,19 +11988,19 @@ function openpgp_packet_signature() { case 21: // Preferred Hash Algorithms this.preferredHashAlgorithms = new Array(); for (var i = 0; i < subplen-1; i++) { - this.preferredHashAlgorithms = input[mypos++].charCodeAt(); + this.preferredHashAlgorithms = input.charCodeAt(mypos++); } break; case 22: // Preferred Compression Algorithms this.preferredCompressionAlgorithms = new Array(); for ( var i = 0; i < subplen-1; i++) { - this.preferredCompressionAlgorithms = input[mypos++].charCodeAt(); + this.preferredCompressionAlgorithms = input.charCodeAt(mypos++); } break; case 23: // Key Server Preferences this.keyServerPreferences = new Array(); for ( var i = 0; i < subplen-1; i++) { - this.keyServerPreferences = input[mypos++].charCodeAt(); + this.keyServerPreferences = input.charCodeAt(mypos++); } break; case 24: // Preferred Key Server @@ -11994,7 +12021,7 @@ function openpgp_packet_signature() { case 27: // Key Flags this.keyFlags = new Array(); for ( var i = 0; i < subplen-1; i++) { - this.keyFlags = input[mypos++].charCodeAt(); + this.keyFlags = input.charCodeAt(mypos++); } break; case 28: // Signer's User ID @@ -12004,7 +12031,7 @@ function openpgp_packet_signature() { } break; case 29: // Reason for Revocation - this.reasonForRevocationFlag = input[mypos++].charCodeAt(); + this.reasonForRevocationFlag = input.charCodeAt(mypos++); this.reasonForRevocationString = new String(); for ( var i = 0; i < subplen -2; i++) { this.reasonForRevocationString += input[mypos++]; @@ -12015,8 +12042,8 @@ function openpgp_packet_signature() { return subplen+1; case 31: // Signature Target // (1 octet public-key algorithm, 1 octet hash algorithm, N octets hash) - this.signatureTargetPublicKeyAlgorithm = input[mypos++].charCodeAt(); - this.signatureTargetHashAlgorithm = input[mypos++].charCodeAt(); + this.signatureTargetPublicKeyAlgorithm = input.charCodeAt(mypos++); + this.signatureTargetHashAlgorithm = input.charCodeAt(mypos++); var signatureTargetHashAlgorithmLength = 0; switch(this.signatureTargetHashAlgorithm) { case 1: // - MD5 [HAC] "MD5" @@ -12094,14 +12121,32 @@ function openpgp_packet_signature() { if (this.version == 4) { this.verified = openpgp_crypto_verifySignature(this.publicKeyAlgorithm, this.hashAlgorithm, this.MPIs, key.obj.publicKeyPacket.MPIs, data+this.signatureData+trailer); + } else if (this.version == 3) { + this.verified = openpgp_crypto_verifySignature(this.publicKeyAlgorithm, this.hashAlgorithm, + this.MPIs, key.obj.publicKeyPacket.MPIs, data+this.signatureData); + } else { + this.verified = false; } break; case 1: // 0x01: Signature of a canonical text document. + var tohash = data + .replace(/\r\n/g,"\n") + .replace(/[\t ]+\n/g, "\n") + .replace(/\n/g,"\r\n"); + if (openpgp.config.debug) { + util.print_debug('tohash: '+util.hexdump(tohash)); + util.print_debug('signatureData: '+util.hexdump(this.signatureData)); + util.print_debug('trailer: '+util.hexdump(trailer)); + } if (this.version == 4) { this.verified = openpgp_crypto_verifySignature(this.publicKeyAlgorithm, this.hashAlgorithm, - this.MPIs, key.obj.publicKeyPacket.MPIs, data+this.signatureData+trailer); - return this.verified; + this.MPIs, key.obj.publicKeyPacket.MPIs, tohash+this.signatureData+trailer); + } else if (this.version == 3) { + this.verified = openpgp_crypto_verifySignature(this.publicKeyAlgorithm, this.hashAlgorithm, + this.MPIs, key.obj.publicKeyPacket.MPIs, tohash+this.signatureData); + } else { + this.verified = false; } break; @@ -12110,13 +12155,12 @@ function openpgp_packet_signature() { // It is calculated identically to a signature over a zero-length // binary document. Note that it doesn't make sense to have a V3 // standalone signature. - if (this.version == 3) { - this.verified = false; - break; - } - - this.verified = openpgp_crypto_verifySignature(this.publicKeyAlgorithm, this.hashAlgorithm, + if (this.version == 4) { + this.verified = openpgp_crypto_verifySignature(this.publicKeyAlgorithm, this.hashAlgorithm, this.MPIs, key.obj.publicKeyPacket.MPIs, this.signatureData+trailer); + } else { + this.verified = false; + } break; case 16: // 0x10: Generic certification of a User ID and Public-Key packet. @@ -12227,6 +12271,7 @@ function openpgp_packet_signature() { // document) that cannot include a target subpacket. default: util.print_error("openpgp.packet.signature.js\n"+"signature verification for type"+ this.signatureType+" not implemented"); + this.verified = false; break; } return this.verified; @@ -12297,7 +12342,7 @@ function openpgp_packet_signature() { function getIssuer() { if (this.version == 4) return this.issuerKeyId; - if (this.verions == 4) + if (this.version == 3) return this.keyId; return null; } @@ -12381,27 +12426,27 @@ function openpgp_packet_userattribute() { while (len != total_len) { var current_len = 0; // 4.2.2.1. One-Octet Lengths - if (input[mypos].charCodeAt() < 192) { - packet_length = input[mypos++].charCodeAt(); + if (input.charCodeAt(mypos) < 192) { + packet_length = input.charCodeAt(mypos++); current_len = 1; // 4.2.2.2. Two-Octet Lengths - } else if (input[mypos].charCodeAt() >= 192 && input[mypos].charCodeAt() < 224) { - packet_length = ((input[mypos++].charCodeAt() - 192) << 8) - + (input[mypos++].charCodeAt()) + 192; + } else if (input.charCodeAt(mypos) >= 192 && input.charCodeAt(mypos) < 224) { + packet_length = ((input.charCodeAt(mypos++) - 192) << 8) + + (input.charCodeAt(mypos++)) + 192; current_len = 2; // 4.2.2.4. Partial Body Lengths - } else if (input[mypos].charCodeAt() > 223 && input[mypos].charCodeAt() < 255) { - packet_length = 1 << (input[mypos++].charCodeAt() & 0x1F); + } else if (input.charCodeAt(mypos) > 223 && input.charCodeAt(mypos) < 255) { + packet_length = 1 << (input.charCodeAt(mypos++) & 0x1F); current_len = 1; // 4.2.2.3. Five-Octet Lengths } else { current_len = 5; mypos++; - packet_length = (input[mypos++].charCodeAt() << 24) | (input[mypos++].charCodeAt() << 16) - | (input[mypos++].charCodeAt() << 8) | input[mypos++].charCodeAt(); + packet_length = (input.charCodeAt(mypos++) << 24) | (input.charCodeAt(mypos++) << 16) + | (input.charCodeAt(mypos++) << 8) | input.charCodeAt(mypos++); } - var subpackettype = input[mypos++].charCodeAt(); + var subpackettype = input.charCodeAt(mypos++); packet_length--; current_len++; this.userattributes[count] = new Array(); @@ -12551,13 +12596,7 @@ function openpgp_packet_userid() { */ this.read_packet = function(input, position, len) { this.packetLength = len; - - var bytes = ''; - for ( var i = 0; i < len; i++) { - bytes += input[position + i]; - } - - this.set_text_bytes(bytes); + this.set_text_bytes(input.substr(position, len)); return this; } @@ -12941,7 +12980,7 @@ function openpgp_type_mpi() { function read(input, position, len) { var mypos = position; - this.mpiBitLength = (input[mypos++].charCodeAt() << 8) | input[mypos++].charCodeAt(); + this.mpiBitLength = (input.charCodeAt(mypos++) << 8) | input.charCodeAt(mypos++); // Additional rules: // @@ -13062,17 +13101,17 @@ function openpgp_type_s2k() { */ function read(input, position) { var mypos = position; - this.type = input[mypos++].charCodeAt(); + this.type = input.charCodeAt(mypos++); switch (this.type) { case 0: // Simple S2K // Octet 1: hash algorithm - this.hashAlgorithm = input[mypos++].charCodeAt(); + this.hashAlgorithm = input.charCodeAt(mypos++); this.s2kLength = 1; break; case 1: // Salted S2K // Octet 1: hash algorithm - this.hashAlgorithm = input[mypos++].charCodeAt(); + this.hashAlgorithm = input.charCodeAt(mypos++); // Octets 2-9: 8-octet salt value this.saltValue = input.substring(mypos, mypos+8); @@ -13082,7 +13121,7 @@ function openpgp_type_s2k() { case 3: // Iterated and Salted S2K // Octet 1: hash algorithm - this.hashAlgorithm = input[mypos++].charCodeAt(); + this.hashAlgorithm = input.charCodeAt(mypos++); // Octets 2-9: 8-octet salt value this.saltValue = input.substring(mypos, mypos+8); @@ -13090,16 +13129,16 @@ function openpgp_type_s2k() { // Octet 10: count, a one-octet, coded value this.EXPBIAS = 6; - var c = input[mypos++].charCodeAt(); + var c = input.charCodeAt(mypos++); this.count = (16 + (c & 15)) << ((c >> 4) + this.EXPBIAS); this.s2kLength = 10; break; case 101: if(input.substring(mypos+1, mypos+4) == "GNU") { - this.hashAlgorithm = input[mypos++].charCodeAt(); + this.hashAlgorithm = input.charCodeAt(mypos++); mypos += 3; // GNU - var gnuExtType = 1000 + input[mypos++].charCodeAt(); + var gnuExtType = 1000 + input.charCodeAt(mypos++); if(gnuExtType == 1001) { this.type = gnuExtType; this.s2kLength = 5; @@ -13219,7 +13258,7 @@ var Util = function() { var c=0; var h; while(c

"+str.replace(/\n/g,"
")+"

"); + this.print_output(this.printLevel.debug, str); } }; @@ -13379,8 +13419,7 @@ var Util = function() { this.print_debug_hexstr_dump = function(str,strToHex) { if (openpgp.config.debug) { str = str + this.hexstrdump(strToHex); - str = openpgp_encoding_html_encode(str); - showMessages("

"+str.replace(/\n/g,"
")+"

"); + this.print_output(this.printLevel.debug, str); } }; @@ -13394,8 +13433,7 @@ var Util = function() { * containing the HTML encoded error message */ this.print_error = function(str) { - str = openpgp_encoding_html_encode(str); - showMessages("

ERROR: "+str.replace(/\n/g,"
")+"

"); + this.print_output(this.printLevel.error, str); }; /** @@ -13408,15 +13446,42 @@ var Util = function() { * containing the HTML encoded info message */ this.print_info = function(str) { - str = openpgp_encoding_html_encode(str); - showMessages("

INFO: "+str.replace(/\n/g,"
")+"

"); + this.print_output(this.printLevel.info, str); }; this.print_warning = function(str) { - str = openpgp_encoding_html_encode(str); - showMessages("

WARNING: "+str.replace(/\n/g,"
")+"

"); + this.print_output(this.printLevel.warning, str); }; + this.print_output = function(level, str) { + var html; + str = openpgp_encoding_html_encode(str).replace(/\n/g,"
"); + if (level == this.printLevel.debug) { + html = "

"+str+"

"; + } else { + var color, heading; + switch (level) { + case this.printLevel.error: + color = "FF8888"; + heading = "ERROR"; + break; + case this.printLevel.warning: + color = 'FFAA88'; + heading = "WARNING"; + break; + case this.printLevel.info: + color = '88FF88'; + heading = 'INFO'; + break; + } + html = "

"+heading+":"+str+"

"; + } + if (typeof showMessages === "function") { + // only call function if defined + showMessages(html); + } + } + this.getLeftNBits = function (string, bitcount) { var rest = bitcount % 8; if (rest == 0) diff --git a/test/new-unit/pgp-test.js b/test/new-unit/pgp-test.js index 9ad80af..00cf5f3 100644 --- a/test/new-unit/pgp-test.js +++ b/test/new-unit/pgp-test.js @@ -118,8 +118,7 @@ define(function(require) { }); describe('Encryption', function() { - var message = 'Hello, World!', - ciphertext; + var message = 'Hello, World!'; beforeEach(function(done) { pgp.importKeys({ @@ -151,7 +150,7 @@ define(function(require) { }); }); - describe('Encrypt', function() { + describe('Encrypt and sign', function() { it('should fail', function(done) { var input = null; @@ -166,13 +165,23 @@ define(function(require) { pgp.encrypt(message, [pubkey], function(err, ct) { expect(err).to.not.exist; expect(ct).to.exist; - ciphertext = ct; done(); }); }); }); - describe('Decrypt', function() { + describe('Decrypt and verify', function() { + var ciphertext; + + beforeEach(function(done) { + pgp.encrypt(message, [pubkey], function(err, ct) { + expect(err).to.not.exist; + expect(ct).to.exist; + ciphertext = ct; + done(); + }); + }); + it('should fail', function(done) { var input = 'asdfa\rsdf';