Decryption via Multi processing in javascript - javascript

I have image data(210KB) that has been encrypted via RSA algorithm. I would like to decrypt that encrypted image data into Javascript using JSEncrypt. I was able to decrypt the image successfully, but it took around 10 seconds. So I thought to use the multiprocessing in Javascript in which I want to use JSEncrypt functionality which is loaded in script.
How to use parallel processing in Javascript to speedup the processing. Or is there any other way to speedup the RSA decryption in Javascript?
Here is the sample code that I was trying for parallel processing in Javascript, but decrypt.decrypt was not working.
<html>
<head>
<script src="D:\parallel.js"></script>
<script src="D:\jsencrypt.js"></script>
</head>
<script>
var decrypt = new JSEncrypt();
decrypt.setPrivateKey('-----BEGIN RSA PRIVATE KEY-----\n'+
'MIICXAIBAAKBgQCsqVGJUP6PhkjLSk3BkVRxg9ikUMZjN1BLS+HmEcjlVbkbsSuO\n'+
'K9uMmtZ7rkmL5aXcLxzNS1fexlr/ixvHeaK43KDXxob5drxcwCaRpadm4JjEKWiG\n'+
'ExbRUIorzFRqxCKFTwXRLerXhyFrfTqv/XNA2F94XwWBNuE8cs9S0ude9QIDAQAB\n'+
'AoGAPMzq/3XcDmJ1I9EojG9G0ypgkYw4MBv8VGeGRuQgYFHNe2jqM4hSKbMksCzx\n'+
'jSfzPhQBCnHroXEr/izYPWgh2m73737TYMHN5UVp9Kt/D1Kg/CUu6SDB6L6hbSYK\n'+
'YkWwifBtPzNcpF9cy7DPVaFGEeCkBroy86VcoQ/cVR7Vk8ECQQDapO2ve9Y332al\n'+
'iW7+2rFxSOeXwdofAlGpv378rAUIWrkGH11a4AtXInaPlty3td225IE6SyJu6trU\n'+
'4ookVNtZAkEAyikwrR6fDOqBPNrVKclfLjdFdfOhVIgdDXcD9lUhmcLJxF9NPKAW\n'+
'cVeCloFuw8eaHv8h2SrUt2itTw/N3ERY/QJAXP8XhbNTezJPM4uQJWAZZwjOUJMI\n'+
'VnYjC+NCfPAht9r2pa8DgxqWWDp1WT+eo5j8M8VfXc8FV04XQ8MTZL6fCQJBAKCY\n'+
'RCDiuHrsN6p+NOQzIjd2lOl0lu6uClZN+4nOaxjY0qv7AUJt8iYr3INvYuyIPfjt\n'+
'uJfqHH1u3G54IZMfgIkCQHBNzxAMO1upPNl5pbzw8KZyNavk9F+uYa3M54cpIw2H\n'+
'uO34E31dml3/77FAaCe8lIo2ezs+nbYVC2nNQZmGtGs=\n'+
'-----END RSA PRIVATE KEY-----');
var r = new Parallel(["qc7zdvF+HPwCiihmjy1frb4qDhAFf+PNhCdlVQdyLMNKm32j2N161fiPv/IHdXWqN1BSlkcPOXSTMkPCkMF9CHb3F5J7l6aw7fmwisptnMDhnlo4ukS04IiGpF+yu42bACXafHDD/qZsBygBDpdc26scb6bb21msUOGcO20oOyk=","Am4K7lP8gGI6dnbDWyZ4N6w5Foxq3IlUmSuGB1JW8MQqTKeKSoCPsCTqS3xrONLfyegUrQlDvONy/xKtryyuL3a8d5jQtmZ/mcn6D/B0llQsPDo3ie6XGRS++oDuNrMDwLbGZrG5j//eWXfaSRueGGxUHQd5dHTwjb+h1Vu/tK8=","AyHcazMAc5EwYMS8RObBM8c6EX5l7BSQd8V2BVUvCM+6QWZCqV888wx1prPxfjM0zOsPYUaSowMwbbtnoeyRuBX4StT8I0+Rkj/iTXOOU5BILpTi9/s7kVraY0TDHed5ZVNlkoAUrK30ny/VYe7MvjL48ecCD2LPqRGl+/Wejnc=","kzMr9po2vakHM6vuAro64cMAU810+z4HN2Jo4QK4+z+bwHWTA+dPHq6on5oub6d5Ko4fx00nZeBnp/m6oe14kyIiqrF0Z751fZv8uSxQ7f6ar1e6lgTqtOAaGW//6xTPyJlgYaqCO3EgQnuHvQYHa6T3SXWN9M9pwhARUwjOics=","gNuTWUUYI/PfNNBfIofHUQEiPct0nzPkVLJBSufZ5Ilm1YtnloUeeSNmBnvdT9GpLU6Giu5g/fCLGbianBsQ7SUi2ASCYDw6SngY8X4jRBjjFXE8ct7c9S3rhEKSmwPwZ6v7cbpXGTSStuhCYAXmhlV5HwHyxNaIxMRMgWpgLE8=","M5RpmPTrd6s/Ur72Q9N5yxPdP9wWbP1zM9EMpjTZlPgVTX2DQG5bgcEapovyszj+3ceUxmexTcRkSuzEQ9FChK1Tk8IjQMvpbiHNO9Bgx2HXabO/3f9NR+OaxHxZxKSV5pMH2TYofbjzPo5GJZ4CZpObeBEHTKjBgGI+PB/ysMk=","L+K2jMDGOvW3Qt9BI1BB0E4WKkUHS8G6SPAqeozes+DZ1I4FSB5BbAW+yNV/I+YvoLD29Unlym5YvjQBi9+XZlG5SBf9TdTsj8AjBTS3CiQobWtzqNmBAnwpW2zWyJPqO5mTC6DvOADkXHsrKtBqJZCjHdb3f92QTFOlLfuWmsw=","pdK1GSDZC3rTFX8gHeDPTQDnBFxL8h0e2qtxiPKNhuatnvqw180E9nqebjIS15faEKEBK4iWxPwR58s7rH1VCnzzTyUln1bOk1TfHiE5SPp66eb9fUvwUQkw/98B0KkwJ/eMK+nMqqh3eURTYm/AtdwSwk+AQpdhRJtp/TGr/3E=","BKl+JVLvfssawrsUBtOe9KdQ4HxXKzUk24RxqMnukUncGMsTaT5LBeo6ngD04UkREoYuU3YngbR/EcZhEynTeCNsvpo9LhvHKwcxTcfKfc2URFiPK65ZLI4WxdJ02iptGNr3FBk8GkbLCOB3m5LI7POZ2faTe0TejJFIftBjbiE=","pAnR9G7U5T2pIEgiZNluAk7/i2/aDXbBtxD1N3KjMn3eDDTweZzoqFbHua2LRFMQ9Rfe0X481YdmM7rcNjlQ0BstkdkaHcE5PNOVKrxQxW3IAERbcvFy6guc2hslh/SFVhMqS4cVXGd82U0Pq3C9togv3Jgt4S9l9GBG4jKGP08=","FDP5NUmMLVNQmmpxGil0llq/0QeLNTlscuMC4q8/d/NX+0N7nMUB8rSKMtRof7SV7Thr5+tC+VViN1oYL2Hc//gysIyS0OYXJWiOWlTLXp0J7dL133h7G7OWZHX+H6Pl3EcgCPvM5YdtHWX5UFOFeLlnZlxR24Euc1XqFVG1o+c=","qg+5Yd8Tl2FZSZgvLOu/V99NRReXZz/MLzFwXs7Mzt2CsHDMZg3ejLK7FBL2zV7tIwapJH20IxtfRoHismAX2IOFhv8WwC/z3CEMXQXYhJA4kFIVOtSpIbCbyfqMUP27jidoIRAgzmLJzzr4AwD3VTdaJZKUi/Uv/DIWXJTtmVk=","ECicU2n2hVSq4DccqxEIPr61rUoLx9e6n9U5AG86D/Vc2uLpHW6sIoOFeAq/ZCcL/j2cp4oPXxsDiT6mbvF7WtMEVhbpgj2faJQcMGbAds7ycjAlBmm9qzdsE4PH0oAUk2vt9lkCodG87w/+IUltYCbGUncmClBuLsZ9t8rPX2Q=","KpT0qqQ+JzVNkBGmg70D80DYnW47GY5zkF6+CxgW76SSRpou2YxgDfyvJVCVE1qSoow4NFPbEJZMTqDcR33RTYdU9EIykLw6XbLchf9y2tecUGtYjLYCzv3SelwusadNswFUOsm5xsKxafnXkvGIvKyy1XO3snSYT3KVfboYDxg=","KQEPX36hqwKFGSF1n266L8d86hhZgHoZfSTxhAOt7+lQH9obRJoZObEq3WflHbJUGhF/6DMgDbre5YgPCq/0dL6/2taRSr2PkG2DsPM4Pkf63YhN8l6l4D0PHSSZP218Mi1x7ag4aiiu/ZHYuGGi0tcTW8wIWjt5U9sy6v8SHFQ=","eczqC+Q5sB+RgrhInvKSswDH0W5cZ+/mGnjFO7dS8DZ1l77gdCN468REMMnmh9DfIrAIgl+71SdywGggN/ildgACirpGXZQr+1RvB8UtDAUlDusfM2vtwB+Az5lymv4QpUekv66hJL0G/kGU4Z7QXJc31vZz3yRutkZJmOINJ7M=","hlHbyfShPCPdpncZ30RGxvbfXqhjbnXu+EF9LBBeTbzrI0i/tLIKL5fBQ0XOQOsxbBqGqE6F0VCfaGR9hbuM0b6fCLRPrp2LK3LK4qhXJ+Ha8WYAfM0xk/5XHgMcaeNMa1H4NrPX3EL0F4BTsi35yTGFJ2KWOAsDVFDRbYN3sF4=","mual4G7H5zF449HY903kKSk4KADFj6+2Lwa4S0tw5aBCUGxAsm8awYHS4vcXd+AtHUDNtrZUyxC4COLOanNQTQ2fdnf+5lWqqUNTS0F+YnLNndGjBU2RdjvZfiMEhciuxIGb68sx74LfvM07F99jEtKnp1PBL2sttbquYEQztss=","Migv5dRzIFj3PiaHBasj+3N6yOybgDMpEoSkwOki70yytgkU4ovu6UUqlL6m5a5fCO61W4YwiT3vV9AcxlMfBwmxcxJUXOf6O24Mf1Ps24pasHHu8QJ9HAviEq2erLtN69ftI2H0P4mvdrw2zwdLmHQ1pwxlO14TajDmnGjuUkg=","Gz4iPZvRGVe9VFK5rzeXgE0FItidX9iPAbN8UBTgxZcZmugpKDpnsDb/Y9WYAgG1Nvf7jux3QbtTsjsgNACrCZ4trhqW+d8fADURFVkaLvGrUTCxIcvjcnDPuc7sBpqUSZ36mteMhFn42rwaLzmCOATa0/gbrxZjckh1yJr94hY=","BVCa1XEO+9kmA8sEpgvaOvJx2D6Dle/sXcXiHHmgaWXKwl0JKsRmuG/8uC8qLRGLDsXMDSPDZM9tSOFk+SZjNl7I+X3JU9hrTi5Jf/2aTePJObUNffODmqTw0H29Ht8kxzoXQZ8XTeym1VVzdSFZ2co/2UNeiP23wfCEY1Laso4=","BHxfRJHVfzPnsAOlEoURAYQwJYlD5JGxBO5Fimi5Eh2r/41ZdDu1CCDj1cPiNhExb6xJlpK30ViV+4XUjy2+Ajfj3kyQTpTLP/vfiTZB5B0H/N0VBF2R0rxhglX7bMecMpoNTwWtsRuERhZTObEQCBJMF/Q1SxuyRO0mczjiy44=","W/KypEoAM8ZRipsYtP4jjm4i+3rFUwsU+9Ckl7EueOpf44nBTK568h994CTu4/uvYTJ5Tb79GdKQ1uCggQrK2Qc7vXiB+D1AkxgAs1CHAIAGKwkIuDwAdWxt/BEUjJ86pjh6TWpvY83l1s2pd92sFvGpFegsHtqVoyUgkTu2Cb4=","V/C3DczOIiM0kTEdRGRz2cbJWzP0s5MnrdZOOee/Zgid67r+FFKEC2VxbtG6yiXr2KmcIgHr9KVdAudjXxmjIEe9Xp8570DiglYSg7u0vyyClHPfYCZu1GUIjN+eFg9DX8WgXAMsbLVSMhTrRMEXlEvpm2hT+7b2TDceC2Oj8Do=","qrK6efhqToF5Bb+Nc11byzcK7y3rDYewXOYwZ1f4N8r3VoC9QUXO63BzDK+iT7T4RpL/BzywC7cwTS1+ozeaImXgrVSyYVdkWp+dzHh8tKFLrOULCmaRx6kKrKGQTYgJUd6Ajq4gjYT0eXVO0eFfPfnxjF/4oi1oZyPgBFmPLXM=","G7Cq9G1O/YEGtdyJMVmmVDekuGp4zvnqaTY+lCslFWzwfYFCEpo6Oebn16o3Uzdr76kGlaZ3DRHTWlQ5fn8rhDNyYi53cggtdeb1UQQzKBPkXeEipMhAwRTsP/9HD6fVZueVxfOCuskbQU9CxVPk2hiR7qX+q39fXbNsit/JCnE=","nrzc/2ZgBRKg/FzfvrGhrY2DxK59uKxyUJoHRSJ14jGza9mdAzYUJcjuCcjTePH8bNsiQm31glF6kF8Azqs6YDV1GpyHMRaVgUVa+9799vhZsIxOArq8LrQH274vLnvISSgi9gWjbXJObc55q/BE00AAyA7NioGID6jA0qk+Miw=","nBu7mW1HmUCOsSiC6lqwZt9kQzHiwvqS7t6Wf7gblD+YZpS2ykCBYu2WziOsZNHSXoNscEINEUsPE5LkPF/qG4fY2UqyGB/WfKts/6ZZfuotICHFGqRwDTL7QVaGE7Id9UML9aZdx9mHVfIJGOelGTdsiWMG5VjFXM17VE2I/nA=","oiXJ7hdyR12L/BM4iYmPuiw95zccrOssAMm08BJWr9Jh8exy5eHnBwr6QWXqT8dYo27ia05wRhgkXxFw8ak+gZ+6cmJdspeG5ATXn/mKsZNcDucoOdj5x4Jrpuz3ErBR2aOQD3R3apc3O3U5Og6unozkjNXWmji1QTbPdivvGtQ=","XwZCRae9cUkxWPwTnREDq+Rha0FN7t1O/RO3reTZ9bXajy4CvDkdcwaygW3zflZk8WO8/WQfoE9xkUZl23/qJxxr1YV2TZdDTJnZSj6n+PP7KRmzZZUVqkI8CYqp9eh4dE3oHqTrtd3nJY52a5PI6QnM3q4sgVLJQZs80CKpDsc=","WxyA4+otmD2XjwnfYWBkaxTokk3xQZqCGeJ4I0eeISOqy6XXXaJYeZrpeC3t369DVWT8YnM54qA1e9QNn77li4Qgq82DIJBmr5q/rCd+s94SmIYeWZxWPu/q6rxkHYogl/1KoDUJ9BfpF96mSmY9WYTq6KZ6zCKtUb/Ylrjy7hI=","k5Ezr4BOy2/rx71LnW2FhhgMfqMLHfFA9Oim3OEYxrcaGFyxLnbFC8vcjRfwDoALTfUi0o6zO4lxkwLiXzB6YnXDyW1ClSuD6F/4JacgpZo3ABdQA2Arz3sZ0vhBDNEIlM4vaBMJuqquBu4Y9jhIv9xIldaYUTjrWJz4wMtu4tE=","bVk0qNU6PuN6TwZnGLtXUTS0mBOBxjcW9tO22mTfY092d14wiBuDHaz4PNbzW8fBK7coPUjmNtAyobQpkFCuaTOBZ4oFJ5ccNWJFLEhQk4VJYq6Rs1UhL+BQqiial68+2jwpDJuEFBbMBfMS8BTZpBwlodmXTnBI7bFViNdk0sg=","O//CV2ww2HXu3RNEZvOrVjJVu2gSOOrzPtkh26cmMgRpEf900cF2EEffj1AC5JZUVXS6NoaKrUhqvNa0eq65feLe6He1Lv1enzzFMvquxpqIO13dz1cjM70C4wxgcArRFzqlbdfFd6k8gOhXy4i1vuz+uUG9UnyeiapO+ojJwMY=","cHNNtzaqEvUgF31mD2a81hOgJcGk+eL4tyJ7HXhTLw3d5jz0S6tdPgA/wBqWgQd4MS4wtLVXXG18HXCpL01dDkOjS06CAYRGxxQFJZvLvxYs4gsqnXYdT/fJC82HivxiwIoh+5ndLZY172JQI4pe7fjOojWrsQz9O6vXmh91tqU=","UeAUeS5VWElo5G7+On4/BpJywCY55BaSMcByzNpaCcLBfxaHQLSwman4nYLOYCo7DvrivpP8P7rrYtWbxEQuLmhznDU3Xf1FH4hipeY0kjs0q294reTtemhuyeLGx3L0Kr3CP1fvA9YLjC2W44a3nwqFHZ+cGjFiAIKBMDUxN1k=","WjmnSy/lT0NiSXgEhzDNioER/kPQ1AzqV0k+yDVyidSzgce4WsCLI3AOh3LQCQpDwhFEyLXI7O3OiPYS5utlLsQfJxtz7vvCqZt7XFPbJRyuKpEbKb8j5d16IQnExIitYN3N0iPeuNnbbEKtyf9rbsvMH0m/u2wfT+4ofAIuRbw=","O0rlz6xURLgH6MZntvhtulFAHoYgZcBkSuPmoagBSiygrmJDFZAwAyb56t/U/DVQuIis6ZO4iZIixCVh6RcNZAD4ZoAxKNVwB25doZEQ8RBCe4OesGIVM26mdBdGBrMFbBsVYbnyr1h31mNHRqSeCmh+nI+jsBQnYEqRhvcrgWw=","m/is3p/3n3ZrasTp/JxETjBwKn0V7K0TAP19P8+ibTwmOOXMGgZ5tQcORO+6RCS2Zp++SGyWn0NU+U0KXzhCYGJc6m5I5ZUzEbu1OPjUyrO/Tjdd6ECC5SxOPL8svXOiHvLg54kUJXX6THd+szkw9qSl0+Uw35/C8o5IkqjnHwE=","P4U6RzJgPb32yFk6U6oLsQtfa0zaEd9Q5W/t6t4N9Hd3jceIEAONLDoW4MX1smMHdEsZURRVzELrB768xaSLgpTHdhEiNUpOXfknTDulZ5TiLJg+PETI3FZmHLYscnYW/8DCMLB55dD/H3YymZlgQebPHKC7vX+4ZFDg58/rLgw=","EDko4Z8h4jl9pLvhRmTvztokHZV+nuvfcVQtKnGTJgmJbbSfur2+4HHUs/3rnEn30on0iWe5gXX/t65ExwdV5czq7fKUtGZFHQEHErdAg8LwsJsF6adUF5lZ7eANZO9vOmyo3/Zde2O/tuWaemoPkjbEJkyhso/X3SKnwEifKLE=","VZy3TezmtyEdjf21cpTiKhtfJ/L/iQvY2kRpu/6PG0owosia0FWS4kdNC62OknQNaevPGuQzn8ysuFaK+eNR4m+ngBn3KjGdMlyka6mVuqCKhVGvUeHXTjr1gSFDsp+QoGbCCJhSX53NfUhftN/t+IAK+KLDJRCvcjh1Ap0vc0U=","dILSmHS6ugK3Bg6U/YuHQ9TOLmIT/MjG+k4xNz4ayb9Wln9OEyW2MYC4U/lqx5ARO69u/15PuSsWV9dkU3XAXaoBxh0Y+ymnR5zoFgXGeY9lqJxt8dIZi19D0AYnOxIxbofDv8mfEB+6OypDc+mA9w4ebkQ7GTH0bk3hlDMyJEM=","Fhlj31Yo0KStffR9RWYe4IoxNQbrg8W0KIaD7eOfxCDmDQjS6B2EDFg+56esf6w0grHzGsFXMw91D7NHkLbJFp5k84Yd5C6i23/tV+3Qmz2GlrW+Atj56a8VqqpxuSbmgt7VGt/M0RzxS0C0apq0PvT8w02WnWwzsvt+5D+5uMA=","F6Jpq7Av814O4JnclvL9OjthpcyBHDKWrT3W0ZJ6yVg/l0E2GONBz67N7NwMew5WD+oXFO1Ez2cK0ywhbxYBkYe5EoimQUebHd6G/02gZ9D+UtYqo2r/Mw6JQ10ihDHDC4uRlu9ZH0AC58s9iIlTi7jmTfMxY4fA9WlsB0Cdr4c=","k8tqdPIYr4PY2sixPg3Tfz8t0EDi7/GX6LT0dGWILEW8ZR/BaNzoA7AXiWrYqR/xlwljiLiPlHcAFFTqR0eAk1WWXUlK5lxHQAv3sI5al/WJgdFXi1yLRSlaAHl7SDbX4N3G1F5XNm74x3Ie9dS7VpQTey/NipJtyNLa7rF6Fww=","p4Al6Ml5Fb5yVD9oiUfzjKCkVKxF8ChLSkimQsDNmdS3Xb6KpHnbFc1s02OwM9jgxH6u/UXbzsBV44s3STzjlFKMy4kcPh8dAxyuQLgb1RNdXsj6TF+pakNsurIPNNNvKmOK8VcEocRlWfuVrF/ZYMfnF7PrEfCFHn+gRSrPFsA=","HSvma4T4c6M9bhCXGtu6N3tKNwD9G4SuCHm3l0J6phup5utFKqFMVXc50dfcRJPIvfpgbvuMNI9wV+8tD6V/SmKT+s85ynUhr2/nVY+Q2raMjbn+VXJsefQOlyWLUAgB4DzOOqjdTtJrN90Isk0nCd2O9cAk2X4bBkP8JumszyA=","PoC+tajeijH20hk1WIroEsHH+rDYIUZmMXuqt2moX2rHJyOZhZWtCapud11GzwRK3cGSBQEmSnEtoCJO+LPRKYOi5qtAh1q6Kjrnj65TdXYlqYwmv4NpDi3NBGA0q/j6ibnswp5BcSoGhD7fSRc2vcaM4cBY2ygmN2hAcazIpeU=","e07WqMiu7Jvo/fX/r7z2HgqqU0twF63Cwze8zqfbpq1F3C9X4JyhPxJb6rhUoV8Wuvlbl8a8JlVqHnV4O82pUrMzTykgSFqgwvIXgA4QP8wtPhyZiDsm0cQOXt4kSiga47xJs40WiF79qg3h8AFLXyE+MdBUULkHzb2g3gyeYEM=","NOu/JVCSRYal1Hw2vpzWKUooDcxiz8pQqOpaDQVgjWTz0qkPWnELJuJ6CS4vUO525OhEIdy2P/VKV8aqhMoP3UAQkVPFFnjhcFgX+hpgWDWVfSz+rrrLYn9Dbu4BMA0Cldulv1Sd92FX0BbFomKWO+TmilQjE4HcqicsWc6uc5s=","R1jbDGZ06k2tdqwfYbW8LCfA5b5RYwKmROsthpEOmskPqDVqHmoT/lxnI0E4FpOpIWTXbQCsAO7JYuzNlnJ6ck80+auWrGzpqrlbauThyzvAZ/X45pc3YT3YwmlqFLDvSYXpUAiMYWExPTWYqCJthApqjzQ/UB47s7x6+D5BWrU=","jqBBt+ngGVsj0inDUgQSQ1P3TgyUNLQMdpSQ2mzcAwghzjJGs3w1FsxUfdL8sZ5De2FPLv03YfOShO0bG8N7qHQngnvDp16qiEms+a5V4MiyiAl8gHFfqP5K/AZhyiEr4SzIsO0l0qQiQeyHNY9onfNLRTH1p85aVMN0Xugxk4o=","dxlgTUdy0EfLWjW0TcyKLfocrc4m7aKEXblep9bzsQ/AmayLbxGiTixin6/WifeHPwXMzDVYHvEaN572GGRt6HRb3IkJc4urPHTIe1pgx88fXU8Ku+MMIixn9FvKkZniiQPnDmMiu0J/PtueFXd7lxDE+/yeK36K6ezhgGBjIWI=","AKqu94tL/qEDdEJY7tz6NuwFYSsvwNGTJ7vgGSOWQ3YKWjR2bIWzBcmekZglkP2laBZwWxLZrdCLuaY0aMolN20mV+TqdKEg86tNQ3HKX2CyZZOAMT9VSi/eHOTL8dczQh9DCIydJyXysxBctD54z/RoL2eAKu6Wx4fEoTCuiCE=","X+VpgInlMxzNqQm1A9RTCqwJgF2TaBqoW8hGnVz7pvjF5esmI7hdpWDzvFre58uKc5I2n/WmkWYYwRwTZP7RMUtG8tE1dUjC2ozTOjGuLrFab6Wik/FwcsIXoRE0gFt8oSg6/0B37xEv/TndZxE0G22b0GRQWD5YN6KTFJ6e5U0=","d5q1xGtrJtBbP/Rn9zMcCj5LG4jWd1CoWEpiIqBkyy4jbicOCcPolssZId8yaXYB2v4g8m+q0lcz0JrwS5qdABLLg/gQfkgYSagYEpQhw0PEGp3tDYh+Py1WW9Id/ZbGvAGuyc1+y/vykvW8aT06BrrWjOvdyb94Xtsl02Ak6Es=","PzRgvVjL8JciNA8Ev78ZxkFaVTpT1rQpPOnTO6p49pY7zTymAXOuPGEGjUgeTCVmOp7cRNW0+J5eRnt9AV2xdURMfkxXaC31L4esO4n+oGCIJ5kmnI4UoZbVI6JJamb3OhiupEcXMZaGaCvAACVsLiQ3Hx6cz6i3XMywYdH+syk=","TO9WBtE3Mh0Kk/YRbfXvVNJiecTRVIkCfC1vqvyRymRgLd5UZxiJwsO3VQ7lZccpesprGPUAHRg+biRwrpksZ8f/oeFqZAt8jZsutJj+bDkQkZvi7pgvy9uuaJ8VB/LHM0+P1vQQn0HIffjdXxIqce3xTO5fIibV9diLVb66+xA=","gpHf2CvGc2lRB8nObYYYH/gdV/pHMu5SpKew2LsVw49lwBrryRJkImK29EKrH90ar8ZOparzY0p4BSHjhjqzHhGfLfjg6Wyhq3OfAmdT1mOktz1FtMkbLTE3jutz1oE5pS3fUmH4wSA1nMz/dKboVUQHazsJHDf/L6aNH5xljyo=","WhgufHL7CNPftNtH5yCNDwtKpVf0fazGv4KY5M7WffTdkaVVfDX4JsSxyNFBPOYP0YqII7qwkSwZiQIjzbsJ1/CWv7B6lSEqDOH9qt3OGx5DWFsMK/I2BqZmaboSdzTK2SGrzy5n6fMWGJv3vUgBNsH71MigSoUfdM+o73GNqSw=","eZNBdVL3EK8sqBRuCVUDq7yIaNqNvcv0hqfweEolia0cAXLs0ZzJzfT3gpcek6y1m+O4sRNRGW4CkIF0yZE9aVe894cL7Wm66y+TX7EpmMd3c6Dg0s4/e0oq2fFWs9V/Z+kWmO33LY0Z9nlJoY9peMutN2ChYlkdwSDllAIJN+o=","TU7K+ORHQE77TKw6HQfPXRrn1/4fVJc7Jytl/+/hcB9uguvOjkP+jnGjz0dENKtZid9ow6Sp5EMfkOW7Cz9mmdvYgbISAM/IobwugMvVwz6ohynmenxYY9571UC9yjY56BhWjRidxAMavF/5Iilv5FiuZRP1u6CLzihkl8Sjmf0=","S4iqDR9x9MvNThJth73ZWlaCej3HIXkIfN4IMV9gOYbCc3e2WFOcv9zB5hRh8iEDLqOIe0zRmSV/lmRBdp5BuHUPYdpzkVPZHuC1MD3stKLcLtL6HEyAV4izeDi7iOJCddDX0EIPxwJHJhpEFV4gX7Qx7UBLmf7/ruQLHts6B1E=","QHi+H59vxGX65F8q9ZB8LFIT+1MXQW/pDoRRoWa9y6K7Xbk1+bI8cqa8FRC+vwO4zSlZd3PMMC+EgdJzj1S+mqpNmo2APJ7lxwxHn3Vs2Vw2/i+qOLdOwxlcATmp0WTsMBZ5C2gDPFkx1VqGfUMaHZOxjjf0O5hYpKkD4v0jSJs=","A9mAf8HtpsOrIFvLM8mjYEAWLgGXtn3nnxGB3MkFOqMBEwou0o85wMWhAORCyd8EkI0otEM8iRc4j+QVYxKXs/p4FMJOfblRo3/Z5ppKOBWXSNsKS7PZm/gjyFmQpOzDlKGhoKch0PzzQ5O45uff2rbNQg9sSyM58AmCz780Eaw=","F/ETESVxG0eLZa36c70nlYYtPwb3j3980zAExAuT50jzUR6JpjkKSqk3yI6rtf3+mlpEVNzVYL49hwV2Jo02bwhr7Cmc/zS/V7v8qtmj+NA1ALpDNGiqjFW9Kj5UmnO2IKq3swg426nlIQt8W22djkW3/DQ5bJCl/WX3HLyRqVk=","qTpQK5CrGWBuJ9fhVXaNg+ujF02eMUmuakWB5U6HN9YdEyj5/VqaFUZbeojPY0asfgfvPm3gPQJYn/FXoSHxQM5vTSVvx/cZ8/TiQgLeZ+eB/09UMTqUSPDNE+R+D98VC/VomMLIQJebw9WVa83kVTEcYHKt44VmssmHEubSOOw=","YzH4OB4r76S/YFPp8VN0cUNTwb75ww5NESVDWFWl4EYWRfWKNH/CJX4SVi4fqhiRyjuwO/38LnAd5GWqTYxo37I1oESixQQLUeBYl8jtyUjBu2HYg6zy8RRAuwwgKVsD3gcbenHI2PwHp0zkOOEOivJqZ4SckudGlfQl2Q1hTEs=","E/bjX7PLBBkzb5SBz+EMDLNGcVM2+nUXM8Yqrwpax8nIStbhrfQWMmwAOQUG5kcC4jSlbrqezr3F3zcqeXs9sx5jA045/GsiL4oCE6ksImZhexdueeqEGO0+ZVHZhVUkRbdR50p+dbDDGf5ta3ZrtnfXqRCua4lQowtu42p8g3s=","nFzwyGZI2rSEL8iRj+RkVdKMGQEQq4In3ICTOk/Iop7dCC6oocHz82p+Fgm/EOYYAoL/SHQOzegBqIVAKz1Bx0I+Wf9qMXhfkXMTNKKHzWoClJ3PWLR28+cW/2oPQJDq5D32OmBvR02V7rgd72P1XRUCkp41vn0R1tl9px1Rmow=","deFVExlJA/nu9tm5jexmlbwoL0w/DBswGXPA4EeH+kUlPwhm+kp9v8twfWH5/l7DpExwMKtamKHh7R08xeni2LyzbShuiGtrC5Ocqy1WgXYM5STSMiNbRHf9JfY04Y0chZLxc3r5U6DL2JgM0cjT57tYof1Vqi5zKzk9KwBR2w0=","UEthrGKi8xwfpLLoyvlF4gvd1g0LzbskZ4vDH62BRCGEXD4LKjwzoZFlSmggoCA3gCW3FOA6nIvR2U3RnAdDuUDmmiLkOmFbO2jX2hQXlxy2raJIr3V2LqkQODhKDfl93bNXC3kScFbMdV+m85kt+ojnIiaXJgifuU0tnwsaPWE=","GlU82nmfWxxyLRkGJalpAdZduaEBcnOYRX/yYJ+FdEIERk7zUynx9/b5Gor+1K5wPL18JUoix810fTrR7p0mbCIz1ULo6i/xvLRuzZSmmiaHqd88ndIZfRcoCyaVbSwsUBoGqsdFT4DrlfMblXINLnrPReTzi8rfw23AqxcsJyo=","Sa9YB1hCAa7U6CKWrE06Aq3pmyhhumE+slQigol8GlTlhZLkSDW1+ouo13a7oM5muWm5XTXHobUqp6NLiUlskssMaj+fZw/DhS7Zg4x21nMKHapRvKznHYxDd97WWxXjj8R5begHclwzKocsDOt0Q4tkeNaciGwMeJqsydleLpk=","CyUljtJTNpvdDC+8dKZrRpty8ghjzTEf63BRk/Wy22fdhWbh4/hFWe72DjUkAJlSlXKa1YjmNfywmsSvsVBrUnHfGsiHwpVc41rsHTtS65TKzCXlQbMHf3mxoH7AUdpIfWM6Mj3VVJ45K0VjU5sRHG3s/zc4/TfwPxXFYaZAzog=","fwFVoIiOw0zPcKV2SY0azNDgAem9cVPGtBqVHOTpoeraF3+UvfbIfz1q+k04HiCu4EhzRxAWej/qO1bdOhqhrlrM1XLhie4UIWuOvvPU9YhEl10vC1mwxJHcxafV+sV2ecKJgm6eL4Cr75WDCXy9mis3AXNyE8VvcKGI5/VjlDg=","pLX6+eMgTN8Dki0VeQeNeCMt+WdWcFqvJmsRaQIZs9gWqPEexYKZAR37w/Q4J0Fy2xhiIdrYAmKy+rr+QWGxRJD5Bm7ncosy54dMSIRw+2U4jKvWCGNJT+7qoSAjJxI7dEVrMnfJwnF+E5RXlwDDHpsoFrfKlEa2kyWg8N+/bXY=","DX6ovXoKM6yeN7L9VW2eCEzX8hrmhYBl6rW8Fk7HV5/YiLYixgfPeqSVKqlwr4USPprnxt6zPeO7VzdEjX/1JtSnzBmCXAFK4YgTupK7hnG3q8lPlNx1Qg1Jjnkn+UtXD8kikDbd2KXN6Ms7xrgvwle8BJ+CtbkeZ1l6W+QF6gg=","a/UJOceOWKLT+W7i5zxLnEFjnFHUoCAVZ/1YGRYPks9yyucRskxN4ny3PrmpPTO7QgnkrxLkRKRhSJmVMpYcVB4LKLVeIe7HBjDGGPZGoQBzuYDPInLcKELcBPEouyiVYF/hdpBbp7XuZngre4W8KPBtT3qHAnUQRowT5xe8Is4=","ZtjH15IvdAImUfb4QC97D6KhAoCWzuVC5gAIq1LYVNIX+hAdKpxPsRR2/4KAH4Av7Wv/MM8TMAnWDvB67USoneOxm2BYYiibDh26ld7Reu3WEohpymKszxEc0Tc6tOk+XbU7TvU5xQg/6r1ylKZ7lNM9kYabusLqWTLOq4GMM2I=","NQtKhJ3dQaGdaq8Fd6UzExBU84SXIhbT+wGr1PJ5Vs1miQ80J0TgYaYmshYoQlQkvy5hLb1YZdYsp82h/xq3vHPc0Mc9uWQ+172ZpC5AbTePR0rIMs2TZHhR6lDEQp0Cyc1RfEl4RVimOir6yJnfmGKEDYBAQ1Jo2VsRLX4ctCU=","ApyXHMvfDe4Jrh7DXMnMviAZ/xiDEicCT9gtayi3l46CGY5+BXywA0oYtCiSb3dtRJtGyCxN0x7GCLQMkSkO3MpPGDRo+J3/Z8mT5gKczKzdOe/2jtOyrru09FWpVrTjT9m8L34nN+plm3acdmU9pdxZbqXXLP3JDvCBwDd3gi0=","qkCPogYiEkHVP4wM2aTtyb5tcP9gLhdo0zHmHeiBAV8dT/8Mq76XefGVdx2J5EQeMgz6uXPgvK26Qs0xz1Kpz6S4S8wbyiHQuLex5zgNG1+vnqng1JKQMNAj6xdZ/eWdoEEn/IvaxF1FtsbTdgQpGq2ECLbytmn9ADsp+LCzGeY=","b/ZeK8BE24gKjr+Sv4G0v6PBxuJRMD+ljqb2frtY0iwKhVsKdmM7A+LnXF77Q9v/+eBaBJzelxGrsIWzXFIyxjAo99Z4eSLx/K4fSbTfmtb6onQaV/ZVO8IDArcpnIvLE1jwRqZqSgpyD8XmrdAflNm3tHkIg0DOlG20PZDlG4M=","phycwObkFgoiHkvSSzauIHO9ZMU5A0dlm95pzGOAzC02LC4C774tbBD4EfYTKci6VP+kVZioi4dqMcICGrNqK8QOcl5OPyMTOFrPlwxXVvGABnBxrKpy1sJQOD/Iye2lX/0Z1P46Vz0zQrNCcUNaXADGdTHab+U4qsVp7ZyypHE=","ds/mkGSmqjM7d+tZNjgpPmJSt54i5sN0g4NfjAdEf/iNhdXLleyIeyNyvvR3Mf9lDdRhYLXm/spqCH0RdCk2sb53q98KxVSM25rwMejszb1HeFjP6EywxycvcrQmW0T0ucmyAqxqVRWdHSPyg96eEfdDgKkuY+Mnt1VywgaiU+c=","pXuReNx4h/0TQC8BF3B0k8ru18mSfvatUf00OP36piKNhZNPkW685dc7YXFgPzvkyie7CVHcjDsjfY3Ecbcxrsx6HtN8rCwo+NczWBTWNzsod3Bv6ypPoHNxVJLC+Md5FLjet3qd6BQrE1kRe7FDUKsiFIBy2jFmkxhaq3VQn4E=","M188lD4GVbW22azNESkw3Ae/MX1QEDpBs9Eus/dHasxmIeTEh++ICEkjpIXGP30DBRokrWOdSA7yX/eSJOsdLGN4zFhI8fowonAIEIfM+EDrMShAhz7KEAwPFaFh04xj94mDvFniFTDqFjia2Ni2TyabSynDc0wFAKbbAVY/Kp8=","jsAOp30/z4ibLJWEJeVDJCgUSGMf6oIhhgppzChk/aKNn7w+MkflWml+xPUXjFYwOXh85xkhKR/CzulZitB3x+pr1IQo6XCZ/dgXPah9PbOicwEiBqKotTmEbcogYZNjcCUWl3uKgxb++4VsUQHLbWYdP0LrmLG6OksKZoZ/9bU=","Pcxn2k6kLQZPAd5uvrmZ7n2GollvMbWR2FeS77Hi/U4kYWOIxhtM1O4XHe5J5NNIpwluYqwfCgPgR3Ggnd3iwKJqSIG9Eco34kaZhoZUMSL/I3j2ORJr9l3tld87q4pUKpE4FYsOSX7pY5a5tjZ11z57oIf9tQu9FERcltYXZ6U=","JUsfP/ZqZ/LitkkFpKCT9iVJlxZ4CzMZLPwmnquku81ZRRprQeX6s6hXy8zZpmJVnsPZhRFzpgdCInmsCyjWYFHBdL/M2zqXelCwSOESuGPI5J5AsIAgHm3a0AwBSLE9lSuJPoXUV16AYeXx69LUGCTz9etD6sZTadG3EE87Sdg=","mMjHDFvAdXQyRhgQOHNyMTnpPY3JE1O9mH+U5ciAtOmSYAqz2930Pae53doh3TKY3tC6N1MT0ETmDZtHunlnMVI/bBm17IhyhIZAthoIlS+4i53e2aToyJ1vNgivCI4ufEbyQzZ9ytK+MpwnmYE6dMwxnohlw+W+thQs917ZirY=","dHKxaWtDyisj9zFUOaQeQM/YbUJI+HBAiskL6zsn1Ysn1ONKMmGfbn6SYMAuMvt4JrR73+fAluh48wwQvChYi2HiG4qnLW7tXbvyhhiT8MiChrq0IKUJhkMHKMqJGB4VbPrczHzM8jjR5jEyaCH8onfcyyb2RmpmLhf5vlVgPEY=","ij6RqDOu8Gn9hBxFoPtCns6/onG0NatWu0Jg0PDCxVF2zMSSxphD2o7pw+6JnuBmat8nwL6sD/XSm8/3+NH30PHejnGNloRjw97jCwVjuMoy4ICJR2GIaoUdyrzqq580g+BUWNAlUuurIVnjZrR5w2llmp5pP91eNHL/sUTSGvY=","bVTIQEfyAqEffE/4qDi13LsR3doQkdI0BZSRze+DSOm2WsgnySgIy8uugobbCOncvQSgPjerP6vwHzphJLc0TwnNlEDa0LA1RRUCEJY10U9oUplglOdPk5qdKWCgQy4rgeDOLfCLrHmnzb+PJpePXk0FxKdOn7zoPPsbElruU8Y=","pcaNIAbg9i+GAuGxMeHSJ4IfNeG6J4OrbOQP8+6c+zcevl3Gas3ofFdxWiUmx7nfSyI5QMP2Hfk+QXv+bvZKIi8ouxm8LBg9vOAxSiUhUlX8WSO/8m+7AQ1GdpqIl7lSc3fkb1r9byWTvLyW8RFJUrMx8RHARtmZ0y6LxMsuSaA=","IEVFnTLTc8Z/9XXyjPQTTKOgjShjvNSFK0q32dwmPZv/N/KCC7YzlWofAqQ0/nWFSQmaSZtZFM/QqiRAAk3xHQSNRQ09fM0llqdjHqA4EKKlJGkMLQ4ErD0FLrV3Awebn0IMLSFSW7ei+Vz8xOZJGTDctlKcmKqg+0YKE6EI5n8=","axajgNjWeZ9cZjJq4NKhkLb+nicTwNFOM3t2WHufe69Wh0MvC4K3s2jUzoL+qlXhMx0ZpKFg5bj9FfVg7KgUJuaFtVGvYJmaj5aMB6S/3DtWitb6n7IuhlK7fD3AgxUiVvmTY64b7L5/xPOTrCKuSMhG5XwYfGptystXxTSYqfc=","EDQpGb+i0+ENcXIlFvPgCb7/ayUv41SJmQmaXUkWj20pXQvg57Z3k1jh8smm9GbL8WL0YmGexg4d66ojEKHfPd0QK42AptutPYQQg3FkDL+MtFDdNg/9cG7xwXTIKt44XB6Kjla7M6oqAUlfmnHnaIsxUsFq20qDZdR/4NcnxnQ=","UuQZt/YHAepGMgkdWg8RYqWHfzrejB1M8im1Nl8OX8IZ3DCd76RgrE2Qa9atH8e0XwJxvXC1ZjpqgbGOh1IFnBgxKjgWYF+eWd4sG8vlk4LbkVs/AMoZV4azru2ek3zYeqM5b2LQGup0r7HCx9pUYDCB+uyEkKa2aed3kzYoIlI=","WGPa3yO6qAgrAsAorqswncwjixZsOvLzoHRkOT4Jgd+V0XdXyXskOQNYH74v35C/tXGCyXgRik3Er23+K3/NrkWOjSTxXJ4rIwAfLQyFvx9NigskeznJez2+aQjQXnGMubeG7PAwMzn00chGcTLy61xLQw2UYvvaXvsBsxqA7T8=","nHG0mcuOPT75QDJ54kF6HuXsdJOXAoz7K+ioI0F7oUQyTFY13n8f6b4VarUEHkl706LP51jgEy/RnYpCqAFK4iDU6Ama6lhJLWWikOvPC+3CAr0O4dcGfyqFxZICicgzuN4b+Y7tJLystHZDwMcBqq78NadXUIRnDayPL5d0Log=","mvFsu1KRGfYDmJBMb4lq4T+3VGkzYaHPIVCzy7f19aYsFs7aVmeM2hxjNHg0ERzoqrsnZfHcoP9PMkIYw5lR/oSkistwCrWyVbgO85CKOFxh4gSlP3zfg5iJzEHXRThxfK70y/txtSI5JxIdnHNbdWo48kr4JyFYDJw7mrTvmUM=","DAsvdeFDCiyGFSSeqyfCD0d3KvLjnbOTXZcZsyuvh/M0T+qaISrIQljlZy3Gvs98X8rnEBmuFunaGCOMr8EJ4HtkS5MF/XsZMtCoQtRk4Y7AnwuS+8IZ0aT3mBzgQAbu9SccgODDxIvltOO34FXPb2kli8PsNM9LPJAf7cTb2NA=","MpC/80NSahcHATkKzAxJ1SCYROnqsmVOWXRvzWnSTsA8l1Q10HX7JlVUeXrjAQghp7iL2rcG3R2oUzcb4H8VsabTU1oiqi/tz0QkAxc37eYV1Oh05F/+/pasJiTmnPeTTli8Ub4cIijq9dz4RVGG5d6qQy1ILXF8PxDEJkOZ1YI=","NBswb71WYSuaFGmPJO+5Wx1VNKaOJLQZX326ANWZhUOV7Jrxjus03WOPp8d0UN5az3aimWdMP7PIWHZSZMCK4KkmYlqgKF24FXvv0K7YteUGenp42g5KbqBEAgV19Dzc8WeddX5Bc7ml7nlKyhlGIu0PqEnYKqEuXYf9qmxLGpg=","GPcjIoJsgr3yoAx2f17TtIT8ugskdWMCJV8N9I9Z94wMuY9WRkaUDABWB1FGoNxESMWcF49uYiiOEmvYFVghs4//zRgROA4ENvru/hxqVOgoaN5Pum95IGhDaoJMKPwGbJAaCdbuY5+cG5vupCR3SonVMKvjsfsi7CASxYLFF/g=","HKnU/Hz0zgrNz2Y/F/5WnLS8R2MFPEcM35uLSMYsjrA4Baw+gSNGw3iSxaEKRKfFJZrrSEVQzAGhxYVbXblJh2sE7YliOdSAyZ2j2o0kC6BmmmJkGibqi82OYyzKyRErzh8xzRuR5rdkzHCdSPbjkdhAmJuYjHCkQ1AkwrCm8J8=","F9EWp9e5qakVwcqfkveOSrizA3VvXqSpIAx6qgEkd4FONU6lH9NxgOh/IZXFpT2sNAUES4lkum6FBdRQEAgPg+fIBIucyx83uT2Cw7C6sJlpLbbfdM9kweWrYblv3WRFK+E5IiOMZtBhwEaQBS8ZNMrv64VbIHQZ9UXrSH4z/gU=","IP7rUlh6CP+GHyl9J/zp9OX0ZxTZYloM4kZ7UpnSqGgkEdNFDV5zHPjLfg7FyqtjTYdbigWJwUB1KqyZkBgQgkzbw/z0VF9BuGoKJR0Lz6CncUGcFrA/JyqGasyYISaMGywhB7l70wvo2ulUoDxFMVSq6L6UXVvOWl+r8Q6x9/E=","UCZqRhlw4sDoRQ9a6RW05ejpuQlBEFqivjHBCUGWKFJe4GB5EE/cUz0y777pNrvSggF4KksO+OIjmZpi95GiS5uOkQTn9h3Z6XGD9VE6yo/uWHlVVdzd08TmSwQGRqM+NxLFjblmgd6WCrtXLybm3pb4AuDFqu3TgEt/NoZ5SQ0=","Byc+2qLxaZujf3moVYnVwjRjkssxE2PSl8Mrz24EwD4cBuQnatFsnNvzoXUXgc15ioYRJWgGpYIUdJRXZoswAIUISZZ1AKhKx08jLoI3bFdJYtAfxWbTU7LZtcu6uiS45bTiuDKVxlIbTodbsxnPYCMq6kfVX7yiv2EgY0cPTHk=","aVs8i40CGtnyKzmSvhMhVBWgVt1h2sCL/+urupYKvlFk03clCdmFa9zGd9ZP3mxdo1vhAKGGoyR85+oKV9O9L7lPAFlSFNWUNYtIXZ1tOqKVRn+9/ti7oO2yLGGrL5MD/XBO0sIPbVU8M/3PPM7KS6vTz4rFKixbo+Qgcv0ulGE=","FsVg6FpLMc8mdUhqWWBbtRLUnOZuZowJD73VP+3yvN8PVFUOTndULvIm1otrpowXqMXdGEozcEt8xHo444NV98wZfSws+mXexPW718nLg1oBP9gx5YYzIuPXdi54Gc9PYlPkdkp9TJrSFkoyD3xG4r3WXBf2CfM3vV0RyOrOE2w=","BciCcIVwVHkuxY/Q1XZ9thFvwephi7wxInX1slzJjb0MaKlS1+kOSwab3eqkGpxQSdzxpn0t1zprRuVMXmuZ1aw7dKo2zdFH5pF5P235skCV2u3dlt8qK0xYgj6NOWZgqtse+QlFtFcyzZQFSMmbNTM2oGIonR81/cFL4rVc7dg=","QadoMq8sc1wl1tm1IffyIXuzvWizaloZ5XsKybqHP5ItZGLtIsVTP4BNIgVedgfxQTiPmrENe3ExyPeZia8wqblYvLXqUfm5K02xrL15I6jp7ZftNzmhTJqMY8WgHrT/9SmZpPPa1sEGQiVXE5c3EHndppVoIkgAmqf9Ar2Y4ck=","WMtmRXS19GuLmwTlUsdhSFbYRFiqGsA/fDGUR1pQpk1kiFAMcjuJZqb5Swv4vhfoLSpsqdLf+c4MhsKRDGHfDrywV66XVF3ju+tHmv5BoKTx6tMV0+4jsDydRq9qhpGw6oYv+arV7pu43ma9wZu4hO7zNrOBFL07TD6IOO0Jzy0=","B5wDsciclfHpDOwo4l2BNL6KIYoHyvzip/rBgE7EDQdIsYJANlcl461+SqE+faF/QJHuwKh5jyxxzFVGGlDGxJMbO2fnsNH07fF+pw+3rhFkGC6Js2MfULdTNZld7JSakL7VKSIO65ze/tHq1hUtjuGDmopRXDJsf8tKo424vHI=","WHYCopiWkqkYzExrnTovtfmQdV/SLgnpGHZlVCVb3tSboIssteS3jkNGIfylJjN5N4+7HiFFu40xEDORwPnOvskROrLNbk4Obfb94SXxYAOxS2F3HuHv8GimnIeeTZfjeYF+qAG+r30tM2YetiBMIRTuCX8scHhrY9mlA4w6xgM=","AK8YzdYxlGdytVT8P2Ui/VUSGnyYOeBcboIOG7IzfllWJfs21dfpscUGvMrUiKqZS7i6WlXHUSYudlaeZq7cCm3xqkcf4vfwoz6At7RCmiIhWqnqkjjJJhxjIyVIJbrFT7P5VEkK9YrJ3eS9S+5bCjTeUVaWyQxuWjbw2o5gy/8=","mL/cOyxDFRLspoLmF4mmVpy8jHWt8I6uyNaEhcVN5mPeyb85ABbxw/JVjle2dwUhF4Pa+sWplZeIMh76JyUnDxro8TtIgG9WEhrHGOtlY+Hll/dR3WMhCRKYyz9SdGJTgsZYG3H+clos/cjV2T509Zfr+HRdSEZxEeXiL+yCX6U=","BDApLXXwSVSApjCF7T6LjP6R2TAc9QrxGVtS8y0baW+GxfEmCfr/BTDI9IHy2ON0GN6xeJvhRSMe6K8mWcJ6yyf2AlNgEnKyHlccrhBAFJ53QsHM0rcvAkYqVPiFPVXeZcraR8wTx99eCMEBZwTELFp6rnXKuYU09pN+KW9L7xg=","TR1tz36rJrtUYUFOqK7A9Cnk4w7HiuvqDKyrUX8i4jkKNYFsLcfaOgBJApBSEjJGoCiew/WjI3x5vgAG+CTx+LTiRWcluTy21aQXdDEjgiyBYJs5ykbfeomXkcCWNENWVtLU0H/ccgTmBFknYwbfUu4K8ZEJZzQy0Ofy/Z+fOtY=","RX7eAauCqgeVjHpS5buZiTnBVZHgBmxkxqmkbKDbJEgfwl+0On0jBVIQNDwYW/JoR2y8xwbHFToGZ4sP+k1vY6bXCeT7IGgv+GbJaS3VlrD7plk/JzeWTdd9Hu7ApkPp4S43jTKwBrwvUD/2gzOYXoWDt8hpvyCLPWpA1rh5+3c=","cGp6i+siSybph+hRaOcjctCGbH0bCVlkHjej2mHZMPutTBiNueOUeOw5jCm7w9ngc84BBl/90JlA/9U0mTSh8K7yhgi8dgHWK5pDo+BesPiv5r0Ga4hT9+OYIV7wX8yhiDc8izeyR51/m5Sqvwl/KEUm1+wqOa+jh4FwfMoQnms=","MCMmu0NtDQ112SPMOt2RPirgiflHTjTpwVWGPn+uihp6HTvozzB57OsrWR0sVDf2mUfukkiJqE9KmVsx9Uujby5ekCLfCw7GICEn6m3yJDTW2Nrv/sBDtG1J04JOGAFL32+ZU4DYT1XvNqwVY9UXzzlqNHkadO28Es86RrGvSzU=","SzJmkf3exPTbEk6lmOkHiIY/l95h09MFMAjZesEFUbX9LgWAaYMdFn6c8dH+JgDhmu2a+W2EKtIv64/N7t52RabOQVAA/0iGI6LPevhPjiBWD2D5/AoM+xF0Cl+uBrpHCEhoMK5MK28HVgl9EXDaT4KtELeC48TUJI3WivmEhtA=","fD1TDxNGjDhwQRM1pWb9Xmtd0agPE0n5V8+it+qSyqutLDtZ98GsTSXZSjhgA2+x059+MEm0d6r/TKZL2br4iD4JRG22cQfls55zxFiwBi6HfB4Iyc5Mkt6ffwp96A7eEK/da4II4MqPzYf9lold3bW8/8fMTtNF9CwViiSZiiY=","IvJ/9aKWsUrjmxtCtRoaGMTWM1K80Yz8Suxm6wb8MfBqDUG2Hr+MhmTLsKzfAWtHQJgyx9CixOrrxxuvORZDikCOq8Cl+JIjEM+K0V7SUpNbdVIs8pr1Opapc8ro9BHy8+bseMRrUjig92psVxQVK62srjPNrk11PT2XzRtwKYA=","cuASUt2Uc+DqavAMvPnSu3ANPPAzZ6EAximqtWWSbCHj3kX+9gTMLaDMRPsHh3RpoB3SfqFkZLebJoS2OTskIcafCDpadfVGFOENcvlImnBNnBifPZApVi0JdJQA1DFyQQREVBNAwJud4jyB+Mm7T5HMMsyuwF9xRSXIaNHvO1s=","ehhMS+vXJ0PMUVwOprXHmLI7QUz41COBvYMTv0hLvZjsJQx4gSEyLKxalC28ofDvne0/X2DTI4VJAXlhsbTN02ab6KRsfixlsHhl0H9ljM6t/0M+F+4DYAidij6itTpxzEhHQ6em6zTnbJn0H6hHUQhF6gSHgzoCW1bhqrzm7vY=","QEQEpfd18cU+ZakCU9LPsf8GWeg6EllwD12qJEb5SY2fUWzlRC1TmEkaddi0AX/0xRccY14FOuVB6HrqpeAJv2wR32dr1Y9qstwdhrF6P08oSMTL6DDoCiKyOHlnPUb1N5b7/80i96Ebnujtdpa5jMZ3P0NhQFntlaSb497eSIw=","YX0eXLpzR7tSlMjBkdoYzyqWwZ3ojzCS70B9U5cUkTaxLqtwo2jB95nU42aHXToRZrHZvD4k5Oi6RCQRVwM5htoclLa/eCe0tm6McdLbhGbFjywteJ2B6As08TtxRiRcNHCIv0TkyKEeCQOMceU/lNyVrUSQfL32FOHT5LybIkM=","StnkaydVgI0r+mMsUXbBposg5mKu0ysEk2kcnIjaeCH8BBKWcIwysRC53upITmcze+qNCjtg7MXQjguftTbpdBM/hFRPjJ6p058AZNBxtzqDi3jZ01bIk4qkMV7ZcT0kDvmjJd4EoYyLzdM9sb5vtC7zmX7A8OuJyQREZKyJpS8=","XROjq92iUff91yFqYvmA1evnr1vq+E7CUWil4aWrMvAKr5zipYW8BTRoWgCmbYbFqOQt9lcVkI4EIOGSWVhWW6frRcFrn6ZDdyPtepn67KLynr8+C2leDAnb4MiGij/OarDGvLnDQPXDJSgbK993T16Et4tPGcF2li4cRjLMXq4=","NrHgOCjQJQenerVkBXLEjomju5j9ljPQl8KT14x6qK8wSCWk3jCZnXc0E7Tvv0cfu126EOIqxGWE/wlucllI9w0V+BiPE6DJu+s/moMaUow+4Jmp1FHbrFS2Qmbv2RsZ3yQEVwly0Bgtc8ayIerymTAJJJ77D/LleLAdhtOnoBA="]);
log = function () { console.log(arguments); };
function fib(n) {
return n;
};
r.map(decrypt.decrypt).then(log);
</script>
</html>

It is possible to utilize multiprocessing in the browser, but browser support is pretty porous, and it will probably feel like a hack. You'll need to use a browser that creates a new process for each tab, and that supports shared worker threads. So basically, Chrome.
Using a Shared Worker object will allow multiple tabs or Windows to share data. So all you need to do is create a script that decrypts a portion of the image (decrypt.js), and sends the clear data back through the worker. Then, have your main script open a few new tabs running decrypt.js and send each one the data to crunch.
However, there are still nuances. The message passing across processes will impact performance for large objects since they must be copied. This can be relieved using Transferable Objects; essentially, you'll have to manage formatting your data into an array buffer and back again.
Also, the browser will probably give those tabs a low execution priority or stop them all together, depending on the implementation. In that case, you'll have to create a worker thread that posts a message to the main thread at regular intervals, for each tab.
This is all assuming that the encryption algorithm you're using can be done in parallel, and that the JSEncrypt library supports it...
My suggestion: decrypt in a standard, dedicated worker thread and show a loading bar. 10 seconds isn't that bad when there's something pretty to look at ;)

Following worked for us as suggested by Artjom B.
We have used AES and RSA encryption here.
<script>
function load_image()
{
var xhr = new XMLHttpRequest();
var img = document.getElementById("image");
var url = "url";
xhr.open('POST', url, true);
xhr.responseType = '';
xhr.setRequestHeader('Authorization', 'Basic ' + btoa("john:secret"));
xhr.setRequestHeader('Access-Control-Allow-Origin','*');
xhr.onload = function(e) {
var test = JSON.parse(e.currentTarget.response);
var response = test.m_Item1;
response = response.replace('"','').replace('"','');
var decrypt = new JSEncrypt();
decrypt.setPrivateKey('-----BEGIN RSA PRIVATE KEY-----\n'+
'MIICXAIBAAKBgQCsqVGJUP6PhkjLSk3BkVRxg9ikUMZjN1BLS+HmEcjlVbkbsSuO\n'+
'K9uMmtZ7rkmL5aXcLxzNS1fexlr/ixvHeaK43KDXxob5drxcwCaRpadm4JjEKWiG\n'+
'ExbRUIorzFRqxCKFTwXRLerXhyFrfTqv/XNA2F94XwWBNuE8cs9S0ude9QIDAQAB\n'+
'AoGAPMzq/3XcDmJ1I9EojG9G0ypgkYw4MBv8VGeGRuQgYFHNe2jqM4hSKbMksCzx\n'+
'jSfzPhQBCnHroXEr/izYPWgh2m73737TYMHN5UVp9Kt/D1Kg/CUu6SDB6L6hbSYK\n'+
'YkWwifBtPzNcpF9cy7DPVaFGEeCkBroy86VcoQ/cVR7Vk8ECQQDapO2ve9Y332al\n'+
'iW7+2rFxSOeXwdofAlGpv378rAUIWrkGH11a4AtXInaPlty3td225IE6SyJu6trU\n'+
'4ookVNtZAkEAyikwrR6fDOqBPNrVKclfLjdFdfOhVIgdDXcD9lUhmcLJxF9NPKAW\n'+
'cVeCloFuw8eaHv8h2SrUt2itTw/N3ERY/QJAXP8XhbNTezJPM4uQJWAZZwjOUJMI\n'+
'VnYjC+NCfPAht9r2pa8DgxqWWDp1WT+eo5j8M8VfXc8FV04XQ8MTZL6fCQJBAKCY\n'+
'RCDiuHrsN6p+NOQzIjd2lOl0lu6uClZN+4nOaxjY0qv7AUJt8iYr3INvYuyIPfjt\n'+
'uJfqHH1u3G54IZMfgIkCQHBNzxAMO1upPNl5pbzw8KZyNavk9F+uYa3M54cpIw2H\n'+
'uO34E31dml3/77FAaCe8lIo2ezs+nbYVC2nNQZmGtGs=\n'+
'-----END RSA PRIVATE KEY-----');
var uncrypted = decrypt.decrypt(test.m_Item2);
console.log(uncrypted);
var base64Key = uncrypted;
var baseiv = uncrypted;
var iv = CryptoJS.enc.Base64.parse(baseiv);
var key = CryptoJS.enc.Base64.parse(base64Key);
var decryptedData = CryptoJS.AES.decrypt(response, key, {
keySize: 128 / 8,
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
} );
var decryptedText = decryptedData.toString( CryptoJS.enc.Utf8 );
document.getElementById("image").setAttribute("src","data:image/png;base64,"+decryptedText);
};
xhr.send();
}
</script>

Related

Javascript. How To Efficiently Store Secp256k1 Private Key for ECIES Scheme

I've been having a really hard time figuring out how to store a Secp256k1 privateKey from multiple libraries (currently on this one for ECIES encryption: https://npm.io/package/#toruslabs/eccrypto).
I have tried encoding and decoding with base64, many implementations of functions that copy array buffer for input encoded string to localStoarge and corresponding output Uint8Array from localStorage, I tried it with IndexedDB, JSON.stringify and parse do not work with binary data, and so many more variations.
When I go through the array buffer elements individually to copy it into a new Uint8Array, I get a similar private key, but with two missing key/field's (parent and offset) which I believe is why every library I have tried so far returns something a long the lines of "bad private key" when I try generating the public key from them.
I am exhausted and I would like some professional insight for my lack of skill in this particular subject. So how can I store (in any way as long as it's client/local) a Secp256k1 private key in a way that if I call it from that persistent client sided data base, they can be used to generate the public keys?
Apparently, the library that uses the private/public key (in this case being #toruslabs/eccrypto) requires a buffer parameter for the keys.
A simple solution would be to make the NodeJS Buffer available in the browser, through browserify. You will only need to include the NodeJS Buffer class to the window object when creating the browserify file, as shown:
const eccrypto = require('./index');
window.eccrypto = eccrypto;
window.Buffer = Buffer;
Then, generate the bundle file using browserify: browserify main.js -o bundle.js
After this, you will be able to use the Buffer class in your browser, which will make loading the private/public key possible. Sample code here:
<script src="bundle.js"></script>
<script>
const eccrypto = window.eccrypto;
const privateKey = eccrypto.generatePrivate();
const publicKey = eccrypto.getPublic(privateKey);
// hex string output of private key
const hexPrivateKey = privateKey.toString('hex')
console.log(hexPrivateKey); // we can do this as privateKey is a Buffer
// load private key again
const newPrivateKey = Buffer.from(hexPrivateKey, 'hex');
const enc = new TextEncoder();
// code referenced from #toruslabs/eccrypto README
// Encrypting the message.
eccrypto.encrypt(publicKey, enc.encode("my testing msg")).then(function (encrypted) {
// Decrypting the message.
eccrypto.decrypt(newPrivateKey, encrypted).then(function (plaintext) {
console.log("Message:", plaintext.toString());
});
});
</script>
This should be sufficient to store the hex string of the private key in the localStorage or any client-side database/storage that you will be using.

Getting Jetty Websocket talking same language as JavaScript Websocket

I wrote a Jetty Websocket Client which is connecting to a remote server and trying to send JSON requests. The server is rejecting the requests as invalid and closing my connection. However, when I send the exact same JSON over using a Javascript client it works just fine. So now I'm left scratching my head is it Jackson 2's encoding of the JSON vs JSON.stringify()? (diff shows the two JSON outputs as exactly the same, no diffs). Is it a different default configuration between Javascript Websockets and Jetty? I'm definitely missing something, bashing my head against the wall.
Java Side Snippet:
final String wsAddress = "ws://" + wssUrl + "/ws";
final WebSocketClient client = new WebSocketClient();
final WSEventSocket socket = new WSEventSocket(loginRequest);
try {
client.start();
final ClientUpgradeRequest request = new ClientUpgradeRequest();
final URI wsUri = new URI(wsAddress);
logger.info("Connecting to {}", wsUri);
final Future<Session> session = client.connect(socket, wsUri, request);
final String requestjson = mapper.writeValueAsString(wsRequestPojo);
final Future<Void> fut = session.getRemote().sendStringByFuture(requestjson);
...
Javascript side snippet:
var wsRequestJson = {...Use output from Java Side...}
var mySock = new WebSocket("ws://" + wsocketUrl + "/ws");
mySock.onmessage = function(evt) { console.log(evt.data); }; mySock.onclose = function() { console.log("CLOSED"); };
mySock.send(JSON.stringify(wsRequestJson));
The Javascript side works perfectly, the Java side is not encoding the data properly. I've tried abunch of things like JSON to byte array and such no luck. I Wiresharked both transactions and I see the WebSocket pushes and the Responses. In Java I'm seeing a Payload that looks exactly like JSON and Wireshark deciphers it. On the Javascript packets I see some type of encoded data \214P\311n\3020\024... I read a little bit about Masked Payloads and both clients are sending Masked Payloads with Masking-Keys, maybe something to do with that?
Any idea how to get the Java Jetty side to encode the data in a similar fashion? Or even what type of encoding the Javascript side is using? I'm probably over thinking it at this point...
Thanks!
As stated by #Joakim Erdfelt in a comment above the Javascript side is using Sec-Websocket-Extension: permessage-deflate by default. Might not be the best way to set it but I updated my Java code by adding:
request.addExtensions(ExtensionConfig.parse("permessage-deflate"));
Updated Snippet:
final String wsAddress = "ws://" + wssUrl + "/ws";
final WebSocketClient client = new WebSocketClient();
final WSEventSocket socket = new WSEventSocket(loginRequest);
try {
client.start();
final ClientUpgradeRequest request = new ClientUpgradeRequest();
request.addExtensions(ExtensionConfig.parse("permessage-deflate"));
final URI wsUri = new URI(wsAddress);
logger.info("Connecting to {}", wsUri);
final Future<Session> session = client.connect(socket, wsUri, request);
final String requestjson = mapper.writeValueAsString(wsRequestPojo);
final Future<Void> fut = session.getRemote().sendStringByFuture(requestjson);
...
Works like a charm! Thanks!

ActiveXObject to download directly to HDD

Is there a native ActiveX Object or similar that I can use to download a source file straight to my HDD. Currently I'm using the following:
function downloadToFile(url, file) {
var xhr = new ActiveXObject("msxml2.xmlhttp"),
ado = new ActiveXObject("ADODB.Stream");
xhr.open("GET", url, false);
xhr.send();
if (xhr.status === 200) {
ado.type = 1;
ado.open();
ado.write(xhr.responseBody);
ado.saveToFile(file);
ado.close();
}
}
But this feels a bit inefficient for a few reasons:
I'm currently using two objects in place of what could possibly be a single object.
The entire response is stored in memory until its wrote to file. This isn't an issue for the most part until I use it to download fairly large files.
Notes/Edits:
I'm working from within microsoft's MSScriptControl.ScriptControl so many web-based libraries will not help.
I'm not necessarily looking for a single object if an answer is able to write the data to file as it is received.
BITSAdmin
BITSAdmin is a Windows command-line tool for downloading and uploading files using the Background Intelligent Transfer Service (BITS).
Note: In Windows 7, BITSAdmin states it's deprecated in favor of PowerShell BITS cmdlets, and may not be included in future Windows versions.
Syntax:
bitsadmin /transfer job_name url local_name
JScript version:
var oShell = new ActiveXObject("WScript.Shell");
oShell.Run("bitsadmin /transfer myDownloadJob http://upload.wikimedia.org/wikipedia/en/b/bc/Wiki.png C:\\Work\\wikipedia-logo.png");
.NET System.Net.WebClient Class
If you have .NET Framework, you can register the System.Net.WebClient class for COM access:
C:\Windows\Microsoft.NET\Framework\v4.0.30319> regasm System.dll
and then use it like this:
var strURL = "http://upload.wikimedia.org/wikipedia/en/b/bc/Wiki.png";
var strFilePath = "C:\\Work\\wikipedia-logo.png";
var oWebClient = new ActiveXObject("System.Net.WebClient");
oWebClient.DownloadFile(strURL, strFilePath);
Chilkat HTTP Library
Chilkat HTTP ActiveX library (commercial) lets you download files directly:
var strURL = "http://upload.wikimedia.org/wikipedia/en/b/bc/Wiki.png";
var strFilePath = "C:\\Work\\wikipedia-logo.png";
var oHTTP = new ActiveXObject("Chilkat_9_5_0.Http");
// Any string unlocks the component for the 1st 30-days.
var success = oHTTP.UnlockComponent("Anything for 30-day trial");
if (success != 1) {
WScript.Echo(oHTTP.LastErrorText);
WScript.Quit();
}
success = oHTTP.Download(strURL, strFilePath);
if (success != 1)
WScript.Echo(oHTTP.LastErrorText);
cURL
Or how about shelling out to cURL (free, MIT/X derivate license)? Though I guess it counts as two objects because of WScript.Shell:
var oShell = new ActiveXObject("WScript.Shell");
oShell.Run("curl -o C:\\Work\\wikipedia-logo.png http://upload.wikimedia.org/wikipedia/en/b/bc/Wiki.png");

Saving text from website using Firefox extension, wrong characters saved

Sorry about the vague title but I'm a bit lost so it's hard to be specific. I've started playing around with Firefox extensions using the add-on SDK. What I'm trying to to is to watch a page for changes, a Twitch.tv chat window in this case, and save those changes to a file.
I've gotten this to work, every time something changes on the page it gets saved. But, "unusual" characters like for example something in Korean doesn't get saved properly. I think this has to do with encoding of the file/string? I tried saving the same characters by copy-pasting them into notepad, it asked me to save in Unicode and when I did everything worked fine. So I figured, ok, I'll change the encoding of the log file to unicode as well before writing to it. Didn't exactly work... Now all the characters were in some kind of foreign language.
The code I'm using to write to the file is this:
var {Cc, Ci, Cu} = require("chrome");
var {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm");
var file = FileUtils.getFile("Desk", ["mylogfile.txt"]);
var stream = FileUtils.openFileOutputStream(file, FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE | FileUtils.MODE_APPEND);
stream.write(data, data.length);
stream.close();
I looked at the description of FileUtils.jsm over at MDN and as far as I can tell there's no way to tell it which encoding I want to use?
If you don't know a fix could you give me some good search terms because I seem to be coming up short on that front. Since I know basically nothing on the subject I'm flailing around in the dark a bit at the moment.
edit:
This is what I ended up with (for now) to get this thing working:
var {Cc, Ci, Cu} = require("chrome");
var {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm");
var file = Cc['#mozilla.org/file/local;1']
.createInstance(Ci.nsILocalFile);
file.initWithPath('C:\\temp\\temp.txt');
if(!file.exists()){
file.create(file.NORMAL_FILE_TYPE, 0666);
}
var charset = 'UTF-8';
var fileStream = Cc['#mozilla.org/network/file-output-stream;1']
.createInstance(Ci.nsIFileOutputStream);
fileStream.init(file, FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE | FileUtils.MODE_APPEND, 0x200, false);
var converterStream = Cc['#mozilla.org/intl/converter-output-stream;1']
.createInstance(Ci.nsIConverterOutputStream);
converterStream.init(fileStream, charset, data.length,
Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
converterStream.writeString(data);
converterStream.close();
fileStream.close();
Dumping just the raw bytes (well, raw jschars actually) won't work. You need to first convert the data into some sensible encoding.
See e.g. the File I/O Snippets. Here are the crucial bits of creating a converter output stream wrapper:
var converter = Components.classes["#mozilla.org/intl/converter-output-stream;1"].
createInstance(Components.interfaces.nsIConverterOutputStream);
converter.init(foStream, "UTF-8", 0, 0);
converter.writeString(data);
converter.close(); // this closes foStream
Another way is to use OS.File + TextConverter:
let encoder = new TextEncoder(); // This encoder can be reused for several writes
let array = encoder.encode("This is some text"); // Convert the text to an array
let promise = OS.File.writeAtomic("file.txt", array, // Write the array atomically to "file.txt", using as temporary
{tmpPath: "file.txt.tmp"}); // buffer "file.txt.tmp".
It might be even possible to mix both. OS.File has the benefit that it will write data and access files off the main thread (so it won't block the UI while the file is being written).

Online/streaming MD5 algorithm?

Is it possible to access the data as it's coming in with HTML5 using the FileReader API and the onprogress event?
If so, is there an "online" version of MD5 or other fast hashing algorithm so that I can begin computing the hash before the file is fully read?
I would like to compute hashes client-side and send just the hash to the server before sending an entire file to check for duplicates before initiating a file upload.
I am not concerned with support for older browsers at this time.
Edit: I recognize that a hash collision does not guarantee a duplicate file, and the only way to be sure is to check byte-by-byte which would mean I would have to upload the file anyway. The probability is low enough that I'm willing to take this risk; worst case I prompt the user and say "This file already appears to be on the server; are you sure you want to upload it?"
is there an "online" version of MD5 or other fast hashing algorithm so that I can begin computing the hash before the file is fully read?
Yes, you can use sjcl if you want to use SHA. sjcl has no native support for MD5, so you'll have to write it yourself (though I'm sure someone else has done it already). CryptoJS has native MD5 support but is significantly slower.
I recognize that a hash collision does not guarantee a duplicate file [...] The probability is low enough that I'm willing to take this risk;
The probability is low enough that there's better chance of a meteor hitting the Earth and ending human life (thus removing the need for hashing altogether) than for a collision to naturally occur. Unless the user crafted a collision on purpose, of course, since MD5's collision resistance is broken.
Here's a live demo of what I believe you're trying to accomplish, minus the "access data as it comes" part. I am not sure if that's possible. I wrote this a long time ago and it uses CryptoJS, so performance isn't that great but it gets the job done. The important chunks are:
function handleFileSelect(evt)
{
evt.stopPropagation();
evt.preventDefault();
var files = evt.target.files || evt.dataTransfer.files; // FileList object.
for (var i=0, file; file = files[i]; ++i)
{
// this creates the FileReader and reads stuff as text
var fr = new FileReader();
fr.onload = (function(theFile) {
return function (e) {
var hashes = parsePseudoBuffer(e.target.result);
document.getElementById('output').innerHTML += '<br />' + theFile.name + '<br />'
+ 'MD5: ' + hashes.md5 + '<br />' + 'SHA1: ' + hashes.sha1 + '<br />' ;
};
}) (file);
fr.readAsArrayBuffer(file); // ArrayBuffer
}
}
function parsePseudoBuffer(result)
{
var buffs = new Uint8Array(result); // buffer thingie
var md5 = CryptoJS.algo.MD5.create();
var sha1 = CryptoJS.algo.SHA1.create();
var bufsize = 8 * 1024; // 8K buffer
for (var bstart=0, bend=bufsize; bstart < buffs.length; bstart+=bufsize, bend+= bufsize)
{
var data = CryptoJS.lib.WordArray.create(buffs.subarray(bstart, bend));
md5.update(data);
sha1.update(data);
}
md5 = md5.finalize();
sha1 = sha1.finalize();
return {'md5': md5, 'sha1': sha1} ;
}
I did some experimenting. It looks like we can get the last chunk read inside the onprogress event by utilizing the incomplete result on the reader object. It only appears to be accessible if we use reader.readAsArrayBuffer (Chrome only?) or reader.readAsBinaryString. The problem with strings is that if that if you want to take a chunk of it, you have to slice it which makes a copy (very slow).
ArrayBuffers have a .subarray method which creates a view into the buffer, without copying any data. This is exactly what we want. However, it doesn't appear to be available on the base class; and it's not clear from the documentation what happens when we construct a derived class (e.g. Uint8Array) using this buffer, but considering the original buffer is accessible via a readonly property, I'm assuming it's not copying.
Both sjcl and CryptoJS conveniently have .update methods which will take in this ArrayBufferView so that you can update your hash on the fly. Thus, I have come up with the following solution (using jQuery, underscore and sjcl):
$(document).on('drop', function(dropEvent) {
dropEvent.preventDefault();
_.each(dropEvent.originalEvent.dataTransfer.files, function(file) {
var reader = new FileReader();
var pos = 0;
var hash = new sjcl.hash.sha256();
reader.onprogress = function(progress) {
var chunk = new Uint8Array(reader.result, pos, progress.loaded - pos);
pos = progress.loaded;
hash.update(chunk);
if(progress.lengthComputable) {
console.log((progress.loaded/progress.total*100).toFixed(1)+'%');
}
};
reader.onload = function() {
var chunk = new Uint8Array(reader.result, pos);
if(chunk.length > 0) hash.update(chunk);
console.log(sjcl.codec.hex.fromBits(hash.finalize()));
};
reader.readAsArrayBuffer(file);
});
});
Note that this solution presently only works in Chrome and is fairly slow. I think sjcl isn't just hashing the file, but it's key-strengthening it, which really isn't what I want. Will investigate more later.

Categories