making webassembly optional, ensuring retry button works when wrong password is provided
Tested configurations: - browser with WASM support (Firefox 68.0.2) - creates paste with zlib compression, no password - creates paste with zlib compression, with password - reads paste with zlib compression, no password - reads paste with zlib compression, with password + retry button works - reads paste without compression, no password - reads paste without compression, with password + retry button works - browser without WASM support (Chromium 76.0.3809.100, started via `chromium-browser --js-flags=--noexpose_wasm`) - creates paste without compression, no password, but shows WASM warning - creates paste without compression, with password, but shows WASM warning - fails to read paste with zlib compression, no password + shows WASM error - fails to read paste with zlib compression, with password + shows WASM error - reads paste without compression, no password - reads paste without compression, with password + retry button works
This commit is contained in:
parent
7a85900b7c
commit
5471757fa7
4 changed files with 56 additions and 74 deletions
103
js/privatebin.js
103
js/privatebin.js
|
@ -780,15 +780,19 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||
* @private
|
||||
* @param {string} message
|
||||
* @param {string} mode
|
||||
* @param {object} zlib
|
||||
* @throws {string}
|
||||
* @return {ArrayBuffer} data
|
||||
*/
|
||||
async function compress(message, mode)
|
||||
async function compress(message, mode, zlib)
|
||||
{
|
||||
message = stringToArraybuffer(
|
||||
utf16To8(message)
|
||||
);
|
||||
if (mode === 'zlib') {
|
||||
let zlib = (await z);
|
||||
if (typeof zlib === 'undefined') {
|
||||
throw 'Error compressing paste, due to missing WebAssembly support.'
|
||||
}
|
||||
return zlib.deflate(message).buffer;
|
||||
}
|
||||
return message;
|
||||
|
@ -803,16 +807,16 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||
* @private
|
||||
* @param {ArrayBuffer} data
|
||||
* @param {string} mode
|
||||
* @param {object} zlib
|
||||
* @throws {string}
|
||||
* @return {string} message
|
||||
*/
|
||||
async function decompress(data, mode)
|
||||
async function decompress(data, mode, zlib)
|
||||
{
|
||||
if (mode === 'zlib' || mode === 'none') {
|
||||
if (mode === 'zlib') {
|
||||
let zlib = (await z);
|
||||
if (typeof zlib === 'undefined') {
|
||||
Alert.showError('Your browser doesn\'t support WebAssembly, used for zlib compression. You can create uncompressed documents, but can\'t read compressed ones.')
|
||||
return '';
|
||||
throw 'Error decompressing paste, due to missing WebAssembly support.'
|
||||
}
|
||||
data = zlib.inflate(
|
||||
new Uint8Array(data)
|
||||
|
@ -962,12 +966,13 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||
*/
|
||||
me.cipher = async function(key, password, message, adata)
|
||||
{
|
||||
let zlib = (await z);
|
||||
// AES in Galois Counter Mode, keysize 256 bit,
|
||||
// authentication tag 128 bit, 10000 iterations in key derivation
|
||||
const compression = (
|
||||
typeof (await z) === 'undefined' ?
|
||||
typeof zlib === 'undefined' ?
|
||||
'none' : // client lacks support for WASM
|
||||
$('body').data('compression') || 'zlib'
|
||||
($('body').data('compression') || 'zlib')
|
||||
),
|
||||
spec = [
|
||||
getRandomBytes(16), // initialization vector
|
||||
|
@ -997,7 +1002,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||
await window.crypto.subtle.encrypt(
|
||||
cryptoSettings(JSON.stringify(adata), spec),
|
||||
await deriveKey(key, password, spec),
|
||||
await compress(message, compression)
|
||||
await compress(message, compression, zlib)
|
||||
).catch(Alert.showError)
|
||||
)
|
||||
),
|
||||
|
@ -1018,7 +1023,8 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||
*/
|
||||
me.decipher = async function(key, password, data)
|
||||
{
|
||||
let adataString, spec, cipherMessage;
|
||||
let adataString, spec, cipherMessage, plaintext;
|
||||
let zlib = (await z);
|
||||
if (data instanceof Array) {
|
||||
// version 2
|
||||
adataString = JSON.stringify(data[1]);
|
||||
|
@ -1045,20 +1051,29 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||
}
|
||||
spec[0] = atob(spec[0]);
|
||||
spec[1] = atob(spec[1]);
|
||||
if (spec[7] === 'zlib') {
|
||||
if (typeof zlib === 'undefined') {
|
||||
throw 'Error decompressing paste, due to missing WebAssembly support.'
|
||||
}
|
||||
}
|
||||
try {
|
||||
return await decompress(
|
||||
await window.crypto.subtle.decrypt(
|
||||
plaintext = await window.crypto.subtle.decrypt(
|
||||
cryptoSettings(adataString, spec),
|
||||
await deriveKey(key, password, spec),
|
||||
stringToArraybuffer(
|
||||
atob(cipherMessage)
|
||||
)
|
||||
).catch(Alert.showError),
|
||||
spec[7]
|
||||
);
|
||||
} catch(err) {
|
||||
console.error(err);
|
||||
return '';
|
||||
}
|
||||
try {
|
||||
return await decompress(plaintext, spec[7], zlib);
|
||||
} catch(err) {
|
||||
Alert.showError(err);
|
||||
return err;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -4522,7 +4537,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||
|
||||
// if all tries failed, we can only return an error
|
||||
if (plaindata.length === 0) {
|
||||
throw 'failed to decipher data';
|
||||
return false;
|
||||
}
|
||||
|
||||
return plaindata;
|
||||
|
@ -4551,8 +4566,11 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||
if (password.length === 0) {
|
||||
throw 'waiting on user to provide a password';
|
||||
} else {
|
||||
displayDecryptionError('failed to decipher paste text: Incorrect password?');
|
||||
throw 'waiting on user to provide correct password';
|
||||
Alert.hideLoading();
|
||||
// reset password, so it can be re-entered
|
||||
Prompt.reset();
|
||||
TopNav.showRetryButton();
|
||||
throw 'Could not decrypt data. Did you enter a wrong password? Retry with the button at the top.';
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4642,27 +4660,6 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* displays and logs decryption errors
|
||||
*
|
||||
* @name PasteDecrypter.displayDecryptionError
|
||||
* @private
|
||||
* @function
|
||||
* @param {string} message
|
||||
*/
|
||||
function displayDecryptionError(message)
|
||||
{
|
||||
Alert.hideLoading();
|
||||
|
||||
// log detailed error, but display generic translation
|
||||
console.error(message);
|
||||
Alert.showError('Could not decrypt data. Did you enter a wrong password? Retry with the button at the top.');
|
||||
|
||||
// reset password, so it can be re-entered
|
||||
Prompt.reset();
|
||||
TopNav.showRetryButton();
|
||||
}
|
||||
|
||||
/**
|
||||
* show decrypted text in the display area, including discussion (if open)
|
||||
*
|
||||
|
@ -4714,7 +4711,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||
.catch((err) => {
|
||||
// wait for the user to type in the password,
|
||||
// then PasteDecrypter.run will be called again
|
||||
console.error(err);
|
||||
Alert.showError(err);
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -4818,34 +4815,14 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||
'subtle' in window.crypto &&
|
||||
'encrypt' in window.crypto.subtle &&
|
||||
'decrypt' in window.crypto.subtle &&
|
||||
'Uint8Array' in window &&
|
||||
'Uint32Array' in window
|
||||
)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(
|
||||
'WebAssembly' in window &&
|
||||
'instantiate' in window.WebAssembly
|
||||
)) {
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
// [\0, 'a', 's', 'm', (uint_32) 1] - smallest valid wasm module
|
||||
const module = new WebAssembly.Module(Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00));
|
||||
if (
|
||||
!(
|
||||
module instanceof WebAssembly.Module &&
|
||||
new WebAssembly.Instance(module) instanceof WebAssembly.Instance
|
||||
)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
} catch (e) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// not checking for async/await, ES6, Promise or Uint8Array support,
|
||||
// as most browsers introduced these earlier then webassembly and webcrypto:
|
||||
// not checking for async/await, ES6 or Promise support, as most
|
||||
// browsers introduced these earlier then webassembly and webcrypto:
|
||||
// https://github.com/PrivateBin/PrivateBin/pull/431#issuecomment-493129359
|
||||
|
||||
return false;
|
||||
|
@ -4881,7 +4858,9 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||
}
|
||||
|
||||
z = zlib.catch(function () {
|
||||
if ($('body').data('compression') !== 'none') {
|
||||
Alert.showWarning('Your browser doesn\'t support WebAssembly, used for zlib compression. You can create uncompressed documents, but can\'t read compressed ones.');
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -8,9 +8,6 @@ describe('CryptTool', function () {
|
|||
await new Promise(resolve => setTimeout(resolve, 1900));
|
||||
});
|
||||
|
||||
// ensure zlib is getting loaded
|
||||
$.PrivateBin.InitialCheck.init();
|
||||
|
||||
this.timeout(30000);
|
||||
it('can en- and decrypt any message', function () {
|
||||
jsc.assert(jsc.forall(
|
||||
|
@ -21,6 +18,8 @@ describe('CryptTool', function () {
|
|||
// pause to let async functions conclude
|
||||
await new Promise(resolve => setTimeout(resolve, 300));
|
||||
let clean = jsdom();
|
||||
// ensure zlib is getting loaded
|
||||
$.PrivateBin.InitialCheck.init();
|
||||
window.crypto = new WebCrypto();
|
||||
message = message.trim();
|
||||
let cipherMessage = await $.PrivateBin.CryptTool.cipher(
|
||||
|
@ -182,6 +181,8 @@ describe('CryptTool', function () {
|
|||
let message = fs.readFileSync('test/compression-sample.txt', 'utf8'),
|
||||
clean = jsdom();
|
||||
window.crypto = new WebCrypto();
|
||||
// ensure zlib is getting loaded
|
||||
$.PrivateBin.InitialCheck.init();
|
||||
let cipherMessage = await $.PrivateBin.CryptTool.cipher(
|
||||
'foo', 'bar', message, []
|
||||
),
|
||||
|
@ -225,6 +226,8 @@ isWhile : interp (while expr sBody) (MemElem mem) =
|
|||
conseq_or_bottom inv (interp (nth_iterate sBody n) (MemElem mem))
|
||||
`;
|
||||
let clean = jsdom();
|
||||
// ensure zlib is getting loaded
|
||||
$.PrivateBin.InitialCheck.init();
|
||||
window.crypto = new WebCrypto();
|
||||
let cipherMessage = await $.PrivateBin.CryptTool.cipher(
|
||||
key, password, message, []
|
||||
|
|
|
@ -71,7 +71,7 @@ if ($MARKDOWN):
|
|||
endif;
|
||||
?>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/purify-1.0.11.js" integrity="sha512-p7UyJuyBkhMcMgE4mDsgK0Lz70OvetLefua1oXs1OujWv9gOxh4xy8InFux7bZ4/DAZsTmO4rgVwZW9BHKaTaw==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-dsFOXy6/2JHcWi9jwtIIBmAwkRc/2cHDON5YONEo9yFZ7Mt//UFszzk3/kKM77JRDvkHC9gvK/ucgsYT+gyUVw==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-tTyV/T13WEwhn9bKY1N0PVu5UFctbDY1qosTTrslVq0R3vPTPjjxSMbUZ3FZBf01rXu39HPV/ibQiD2fOEjYcA==" crossorigin="anonymous"></script>
|
||||
<!--[if IE]>
|
||||
<style type="text/css">body {padding-left:60px;padding-right:60px;} #ienotice {display:block;}</style>
|
||||
<![endif]-->
|
||||
|
|
|
@ -49,7 +49,7 @@ if ($MARKDOWN):
|
|||
endif;
|
||||
?>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/purify-1.0.11.js" integrity="sha512-p7UyJuyBkhMcMgE4mDsgK0Lz70OvetLefua1oXs1OujWv9gOxh4xy8InFux7bZ4/DAZsTmO4rgVwZW9BHKaTaw==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-dsFOXy6/2JHcWi9jwtIIBmAwkRc/2cHDON5YONEo9yFZ7Mt//UFszzk3/kKM77JRDvkHC9gvK/ucgsYT+gyUVw==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-tTyV/T13WEwhn9bKY1N0PVu5UFctbDY1qosTTrslVq0R3vPTPjjxSMbUZ3FZBf01rXu39HPV/ibQiD2fOEjYcA==" crossorigin="anonymous"></script>
|
||||
<!--[if IE]>
|
||||
<style type="text/css">body {padding-left:60px;padding-right:60px;} #ienotice {display:block;}</style>
|
||||
<![endif]-->
|
||||
|
|
Loading…
Reference in a new issue