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
115
js/privatebin.js
115
js/privatebin.js
|
@ -780,15 +780,19 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||||
* @private
|
* @private
|
||||||
* @param {string} message
|
* @param {string} message
|
||||||
* @param {string} mode
|
* @param {string} mode
|
||||||
|
* @param {object} zlib
|
||||||
|
* @throws {string}
|
||||||
* @return {ArrayBuffer} data
|
* @return {ArrayBuffer} data
|
||||||
*/
|
*/
|
||||||
async function compress(message, mode)
|
async function compress(message, mode, zlib)
|
||||||
{
|
{
|
||||||
message = stringToArraybuffer(
|
message = stringToArraybuffer(
|
||||||
utf16To8(message)
|
utf16To8(message)
|
||||||
);
|
);
|
||||||
if (mode === 'zlib') {
|
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 zlib.deflate(message).buffer;
|
||||||
}
|
}
|
||||||
return message;
|
return message;
|
||||||
|
@ -803,16 +807,16 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||||
* @private
|
* @private
|
||||||
* @param {ArrayBuffer} data
|
* @param {ArrayBuffer} data
|
||||||
* @param {string} mode
|
* @param {string} mode
|
||||||
|
* @param {object} zlib
|
||||||
|
* @throws {string}
|
||||||
* @return {string} message
|
* @return {string} message
|
||||||
*/
|
*/
|
||||||
async function decompress(data, mode)
|
async function decompress(data, mode, zlib)
|
||||||
{
|
{
|
||||||
if (mode === 'zlib' || mode === 'none') {
|
if (mode === 'zlib' || mode === 'none') {
|
||||||
if (mode === 'zlib') {
|
if (mode === 'zlib') {
|
||||||
let zlib = (await z);
|
|
||||||
if (typeof zlib === 'undefined') {
|
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.')
|
throw 'Error decompressing paste, due to missing WebAssembly support.'
|
||||||
return '';
|
|
||||||
}
|
}
|
||||||
data = zlib.inflate(
|
data = zlib.inflate(
|
||||||
new Uint8Array(data)
|
new Uint8Array(data)
|
||||||
|
@ -962,12 +966,13 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||||
*/
|
*/
|
||||||
me.cipher = async function(key, password, message, adata)
|
me.cipher = async function(key, password, message, adata)
|
||||||
{
|
{
|
||||||
|
let zlib = (await z);
|
||||||
// AES in Galois Counter Mode, keysize 256 bit,
|
// AES in Galois Counter Mode, keysize 256 bit,
|
||||||
// authentication tag 128 bit, 10000 iterations in key derivation
|
// authentication tag 128 bit, 10000 iterations in key derivation
|
||||||
const compression = (
|
const compression = (
|
||||||
typeof (await z) === 'undefined' ?
|
typeof zlib === 'undefined' ?
|
||||||
'none' : // client lacks support for WASM
|
'none' : // client lacks support for WASM
|
||||||
$('body').data('compression') || 'zlib'
|
($('body').data('compression') || 'zlib')
|
||||||
),
|
),
|
||||||
spec = [
|
spec = [
|
||||||
getRandomBytes(16), // initialization vector
|
getRandomBytes(16), // initialization vector
|
||||||
|
@ -997,7 +1002,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||||
await window.crypto.subtle.encrypt(
|
await window.crypto.subtle.encrypt(
|
||||||
cryptoSettings(JSON.stringify(adata), spec),
|
cryptoSettings(JSON.stringify(adata), spec),
|
||||||
await deriveKey(key, password, spec),
|
await deriveKey(key, password, spec),
|
||||||
await compress(message, compression)
|
await compress(message, compression, zlib)
|
||||||
).catch(Alert.showError)
|
).catch(Alert.showError)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
@ -1018,7 +1023,8 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||||
*/
|
*/
|
||||||
me.decipher = async function(key, password, data)
|
me.decipher = async function(key, password, data)
|
||||||
{
|
{
|
||||||
let adataString, spec, cipherMessage;
|
let adataString, spec, cipherMessage, plaintext;
|
||||||
|
let zlib = (await z);
|
||||||
if (data instanceof Array) {
|
if (data instanceof Array) {
|
||||||
// version 2
|
// version 2
|
||||||
adataString = JSON.stringify(data[1]);
|
adataString = JSON.stringify(data[1]);
|
||||||
|
@ -1045,20 +1051,29 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||||
}
|
}
|
||||||
spec[0] = atob(spec[0]);
|
spec[0] = atob(spec[0]);
|
||||||
spec[1] = atob(spec[1]);
|
spec[1] = atob(spec[1]);
|
||||||
|
if (spec[7] === 'zlib') {
|
||||||
|
if (typeof zlib === 'undefined') {
|
||||||
|
throw 'Error decompressing paste, due to missing WebAssembly support.'
|
||||||
|
}
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
return await decompress(
|
plaintext = await window.crypto.subtle.decrypt(
|
||||||
await window.crypto.subtle.decrypt(
|
cryptoSettings(adataString, spec),
|
||||||
cryptoSettings(adataString, spec),
|
await deriveKey(key, password, spec),
|
||||||
await deriveKey(key, password, spec),
|
stringToArraybuffer(
|
||||||
stringToArraybuffer(
|
atob(cipherMessage)
|
||||||
atob(cipherMessage)
|
)
|
||||||
)
|
|
||||||
).catch(Alert.showError),
|
|
||||||
spec[7]
|
|
||||||
);
|
);
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
|
console.error(err);
|
||||||
return '';
|
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 all tries failed, we can only return an error
|
||||||
if (plaindata.length === 0) {
|
if (plaindata.length === 0) {
|
||||||
throw 'failed to decipher data';
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return plaindata;
|
return plaindata;
|
||||||
|
@ -4551,8 +4566,11 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||||
if (password.length === 0) {
|
if (password.length === 0) {
|
||||||
throw 'waiting on user to provide a password';
|
throw 'waiting on user to provide a password';
|
||||||
} else {
|
} else {
|
||||||
displayDecryptionError('failed to decipher paste text: Incorrect password?');
|
Alert.hideLoading();
|
||||||
throw 'waiting on user to provide correct password';
|
// 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)
|
* show decrypted text in the display area, including discussion (if open)
|
||||||
*
|
*
|
||||||
|
@ -4714,7 +4711,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
// wait for the user to type in the password,
|
// wait for the user to type in the password,
|
||||||
// then PasteDecrypter.run will be called again
|
// 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 &&
|
'subtle' in window.crypto &&
|
||||||
'encrypt' in window.crypto.subtle &&
|
'encrypt' in window.crypto.subtle &&
|
||||||
'decrypt' in window.crypto.subtle &&
|
'decrypt' in window.crypto.subtle &&
|
||||||
|
'Uint8Array' in window &&
|
||||||
'Uint32Array' in window
|
'Uint32Array' in window
|
||||||
)) {
|
)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(
|
// not checking for async/await, ES6 or Promise support, as most
|
||||||
'WebAssembly' in window &&
|
// browsers introduced these earlier then webassembly and webcrypto:
|
||||||
'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:
|
|
||||||
// https://github.com/PrivateBin/PrivateBin/pull/431#issuecomment-493129359
|
// https://github.com/PrivateBin/PrivateBin/pull/431#issuecomment-493129359
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -4881,7 +4858,9 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||||
}
|
}
|
||||||
|
|
||||||
z = zlib.catch(function () {
|
z = zlib.catch(function () {
|
||||||
Alert.showWarning('Your browser doesn\'t support WebAssembly, used for zlib compression. You can create uncompressed documents, but can\'t read compressed ones.');
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,9 +8,6 @@ describe('CryptTool', function () {
|
||||||
await new Promise(resolve => setTimeout(resolve, 1900));
|
await new Promise(resolve => setTimeout(resolve, 1900));
|
||||||
});
|
});
|
||||||
|
|
||||||
// ensure zlib is getting loaded
|
|
||||||
$.PrivateBin.InitialCheck.init();
|
|
||||||
|
|
||||||
this.timeout(30000);
|
this.timeout(30000);
|
||||||
it('can en- and decrypt any message', function () {
|
it('can en- and decrypt any message', function () {
|
||||||
jsc.assert(jsc.forall(
|
jsc.assert(jsc.forall(
|
||||||
|
@ -21,13 +18,15 @@ describe('CryptTool', function () {
|
||||||
// pause to let async functions conclude
|
// pause to let async functions conclude
|
||||||
await new Promise(resolve => setTimeout(resolve, 300));
|
await new Promise(resolve => setTimeout(resolve, 300));
|
||||||
let clean = jsdom();
|
let clean = jsdom();
|
||||||
|
// ensure zlib is getting loaded
|
||||||
|
$.PrivateBin.InitialCheck.init();
|
||||||
window.crypto = new WebCrypto();
|
window.crypto = new WebCrypto();
|
||||||
message = message.trim();
|
message = message.trim();
|
||||||
let cipherMessage = await $.PrivateBin.CryptTool.cipher(
|
let cipherMessage = await $.PrivateBin.CryptTool.cipher(
|
||||||
key, password, message, []
|
key, password, message, []
|
||||||
),
|
),
|
||||||
plaintext = await $.PrivateBin.CryptTool.decipher(
|
plaintext = await $.PrivateBin.CryptTool.decipher(
|
||||||
key, password, cipherMessage
|
key, password, cipherMessage
|
||||||
);
|
);
|
||||||
clean();
|
clean();
|
||||||
return message === plaintext;
|
return message === plaintext;
|
||||||
|
@ -182,6 +181,8 @@ describe('CryptTool', function () {
|
||||||
let message = fs.readFileSync('test/compression-sample.txt', 'utf8'),
|
let message = fs.readFileSync('test/compression-sample.txt', 'utf8'),
|
||||||
clean = jsdom();
|
clean = jsdom();
|
||||||
window.crypto = new WebCrypto();
|
window.crypto = new WebCrypto();
|
||||||
|
// ensure zlib is getting loaded
|
||||||
|
$.PrivateBin.InitialCheck.init();
|
||||||
let cipherMessage = await $.PrivateBin.CryptTool.cipher(
|
let cipherMessage = await $.PrivateBin.CryptTool.cipher(
|
||||||
'foo', 'bar', message, []
|
'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))
|
conseq_or_bottom inv (interp (nth_iterate sBody n) (MemElem mem))
|
||||||
`;
|
`;
|
||||||
let clean = jsdom();
|
let clean = jsdom();
|
||||||
|
// ensure zlib is getting loaded
|
||||||
|
$.PrivateBin.InitialCheck.init();
|
||||||
window.crypto = new WebCrypto();
|
window.crypto = new WebCrypto();
|
||||||
let cipherMessage = await $.PrivateBin.CryptTool.cipher(
|
let cipherMessage = await $.PrivateBin.CryptTool.cipher(
|
||||||
key, password, message, []
|
key, password, message, []
|
||||||
|
|
|
@ -71,7 +71,7 @@ if ($MARKDOWN):
|
||||||
endif;
|
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/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]>
|
<!--[if IE]>
|
||||||
<style type="text/css">body {padding-left:60px;padding-right:60px;} #ienotice {display:block;}</style>
|
<style type="text/css">body {padding-left:60px;padding-right:60px;} #ienotice {display:block;}</style>
|
||||||
<![endif]-->
|
<![endif]-->
|
||||||
|
|
|
@ -49,7 +49,7 @@ if ($MARKDOWN):
|
||||||
endif;
|
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/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]>
|
<!--[if IE]>
|
||||||
<style type="text/css">body {padding-left:60px;padding-right:60px;} #ienotice {display:block;}</style>
|
<style type="text/css">body {padding-left:60px;padding-right:60px;} #ienotice {display:block;}</style>
|
||||||
<![endif]-->
|
<![endif]-->
|
||||||
|
|
Loading…
Reference in a new issue