implement zlib via web assembly, replacing rawdeflate library

This commit is contained in:
El RIDO 2018-12-27 21:32:13 +01:00
parent 5ce3aa2817
commit 0ad5b3e900
No known key found for this signature in database
GPG key ID: 0F5C940A6BD81F92
8 changed files with 216 additions and 1724 deletions

View file

@ -10,8 +10,8 @@ global.WebCrypto = require('node-webcrypto-ossl');
// application libraries to test // application libraries to test
global.$ = global.jQuery = require('./jquery-3.3.1'); global.$ = global.jQuery = require('./jquery-3.3.1');
global.RawDeflate = require('./rawdeflate-0.5').RawDeflate; global.RawDeflate = require('./rawinflate-0.3').RawDeflate;
global.RawDeflate.inflate = require('./rawinflate-0.3').RawDeflate.inflate; require('./zlib-1.2.11');
require('./prettify'); require('./prettify');
global.prettyPrint = window.PR.prettyPrint; global.prettyPrint = window.PR.prettyPrint;
global.prettyPrintOne = window.PR.prettyPrintOne; global.prettyPrintOne = window.PR.prettyPrintOne;

View file

@ -32,6 +32,13 @@ jQuery(document).ready(function() {
jQuery.PrivateBin = (function($, RawDeflate) { jQuery.PrivateBin = (function($, RawDeflate) {
'use strict'; 'use strict';
/**
* zlib library interface
*
* @private
*/
let z;
/** /**
* static Helper methods * static Helper methods
* *
@ -619,40 +626,67 @@ jQuery.PrivateBin = (function($, RawDeflate) {
} }
/** /**
* compress a string, returns base64 encoded data (deflate compression) * compress a string (deflate compression), returns buffer
* *
* @name CryptTool.compress * @name CryptTool.compress
* @async
* @function * @function
* @private * @private
* @param {string} message * @param {string} message
* @return {string} base64 data * @param {string} mode
* @return {ArrayBuffer} data
*/ */
function compress(message) async function compress(message, mode)
{ {
// detect presence of Base64.js, indicating legacy ZeroBin paste message = StrToArr(
if (typeof Base64 === 'undefined') { utob(message)
return btoa( utob( RawDeflate.deflate( utob( message ) ) ) ); );
} else { if (mode === 'zlib') {
return Base64.toBase64( RawDeflate.deflate( Base64.utob( message ) ) ); return z.deflate(message).buffer;
} }
return message;
} }
/** /**
* decompress a base64 encoded data (deflate compression), returns string * decompress potentially base64 encoded, deflate compressed buffer, returns string
* *
* @name CryptTool.decompress * @name CryptTool.decompress
* @async
* @function * @function
* @private * @private
* @param {string} data base64 data * @param {ArrayBuffer} data
* @param {string} mode
* @return {string} message * @return {string} message
*/ */
function decompress(data) async function decompress(data, mode)
{ {
if (mode === 'zlib' || mode === 'none') {
if (mode === 'zlib') {
data = z.inflate(new Uint8Array(data)).buffer;
}
return btou(
ArrToStr(data)
);
}
// detect presence of Base64.js, indicating legacy ZeroBin paste // detect presence of Base64.js, indicating legacy ZeroBin paste
if (typeof Base64 === 'undefined') { if (typeof Base64 === 'undefined') {
return btou( RawDeflate.inflate( btou( atob( data ) ) ) ); return btou(
RawDeflate.inflate(
btou(
atob(
ArrToStr(data)
)
)
)
);
} else { } else {
return Base64.btou( RawDeflate.inflate( Base64.fromBase64( data ) ) ); return Base64.btou(
RawDeflate.inflate(
Base64.fromBase64(
ArrToStr(data)
)
)
);
} }
} }
@ -779,7 +813,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
* @param {string} password * @param {string} password
* @param {string} message * @param {string} message
* @param {array} adata * @param {array} adata
* @return {array} encrypted message & adata containing encryption spec * @return {array} encrypted message in base64 encoding & adata containing encryption spec
*/ */
me.cipher = async function(key, password, message, adata) me.cipher = async function(key, password, message, adata)
{ {
@ -793,17 +827,11 @@ jQuery.PrivateBin = (function($, RawDeflate) {
128, // tag size 128, // tag size
'aes', // algorithm 'aes', // algorithm
'gcm', // algorithm mode 'gcm', // algorithm mode
'none' // compression 'zlib' // compression
], encodedSpec = [ ], encodedSpec = [];
btoa(spec[0]), for (let i = 0; i < spec.length; ++i) {
btoa(spec[1]), encodedSpec[i] = i < 2 ? btoa(spec[i]) : spec[i];
spec[2], }
spec[3],
spec[4],
spec[5],
spec[6],
spec[7]
];
if (adata.length === 0) { if (adata.length === 0) {
// comment // comment
adata = encodedSpec; adata = encodedSpec;
@ -819,7 +847,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),
StrToArr(utob(message)) await compress(message, spec[7])
) )
) )
), ),
@ -864,28 +892,21 @@ jQuery.PrivateBin = (function($, RawDeflate) {
} else { } else {
throw 'unsupported message format'; throw 'unsupported message format';
} }
compression = encodedSpec[7];
let spec = encodedSpec, plainText = ''; let spec = encodedSpec, plainText = '';
spec[0] = atob(spec[0]); spec[0] = atob(spec[0]);
spec[1] = atob(spec[1]); spec[1] = atob(spec[1]);
try { try {
plainText = ArrToStr( return await decompress(
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),
StrToArr(atob(cipherMessage)) StrToArr(atob(cipherMessage))
) ),
encodedSpec[7]
); );
} catch(err) { } catch(err) {
return ''; return '';
} }
if (compression === 'none') {
return btou(plainText);
} else if (compression === 'rawdeflate') {
return decompress(plainText);
} else {
throw 'unsupported compression format';
}
}; };
/** /**
@ -4487,7 +4508,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
* @name Controller.init * @name Controller.init
* @function * @function
*/ */
me.init = function() me.init = async function()
{ {
// first load translations // first load translations
I18n.loadTranslations(); I18n.loadTranslations();
@ -4505,6 +4526,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
Prompt.init(); Prompt.init();
TopNav.init(); TopNav.init();
UiHelper.init(); UiHelper.init();
z = (await zlib);
// check whether existing paste needs to be shown // check whether existing paste needs to be shown
try { try {

File diff suppressed because it is too large Load diff

View file

@ -2,12 +2,12 @@
require('../common'); require('../common');
describe('CryptTool', function () { describe('CryptTool', function () {
afterEach(async function () {
// pause to let async functions conclude
await new Promise(resolve => setTimeout(resolve, 1900));
});
describe('cipher & decipher', function () { describe('cipher & decipher', function () {
afterEach(async function () {
// pause to let async functions conclude
await new Promise(resolve => setTimeout(resolve, 1900));
});
this.timeout(30000); this.timeout(30000);
it('can en- and decrypt any message', function () { it('can en- and decrypt any message', function () {
jsc.check(jsc.forall( jsc.check(jsc.forall(
@ -29,7 +29,8 @@ describe('CryptTool', function () {
clean(); clean();
return message === plaintext; return message === plaintext;
} }
)); ),
{tests: 3});
}); });
// The below static unit tests are included to ensure deciphering of "classic" // The below static unit tests are included to ensure deciphering of "classic"

144
js/zlib-1.2.11.js Normal file
View file

@ -0,0 +1,144 @@
'use strict';
let ret;
async function initialize() {
if (ret) return ret;
const COMPRESSION_LEVEL = 9;
const NO_ZLIB_HEADER = -1;
const CHUNK_SIZE = 32 * 1024;
const map = {};
const memory = new WebAssembly.Memory({
initial: 1,
maximum: 1024, // 64MB
});
const env = {
memory,
writeToJs(ptr, size) {
const o = map[ptr];
o.onData(new Uint8Array(memory.buffer, dstPtr, size));
},
_abort: errno => { console.error(`Error: ${errno}`) },
_grow: () => { },
};
let ins;
if (typeof fetch === 'undefined') {
const buff = fs.readFileSync('zlib-1.2.11.wasm');
const module = await WebAssembly.compile(buff);
ins = await WebAssembly.instantiate(module, { env });
} else {
ins = await WebAssembly.instantiateStreaming(fetch('zlib-1.2.11.wasm'));
}
const srcPtr = ins.exports._malloc(CHUNK_SIZE);
const dstPtr = ins.exports._malloc(CHUNK_SIZE);
class RawDef {
constructor() {
this.zstreamPtr = ins.exports._createDeflateContext(COMPRESSION_LEVEL, NO_ZLIB_HEADER);
map[this.zstreamPtr] = this;
this.offset = 0;
this.buff = new Uint8Array(CHUNK_SIZE);
}
deflate(chunk, flush) {
const src = new Uint8Array(memory.buffer, srcPtr, chunk.length);
src.set(chunk);
ins.exports._deflate(this.zstreamPtr, srcPtr, dstPtr, chunk.length, CHUNK_SIZE, flush);
}
onData(chunk) {
if (this.buff.length < this.offset + chunk.length) {
const buff = this.buff;
this.buff = new Uint8Array(this.buff.length * 2);
this.buff.set(buff);
}
this.buff.set(chunk, this.offset);
this.offset += chunk.length;
}
destroy() {
ins.exports._freeDeflateContext(this.zstreamPtr);
delete map[this.zstreamPtr];
this.buff = null;
}
getBuffer() {
const res = new Uint8Array(this.offset);
for (let i = 0; i < this.offset; ++i) {
res[i] = this.buff[i];
}
return res;
}
}
class RawInf {
constructor() {
this.zstreamPtr = ins.exports._createInflateContext(NO_ZLIB_HEADER);
map[this.zstreamPtr] = this;
this.offset = 0;
this.buff = new Uint8Array(CHUNK_SIZE);
}
inflate(chunk) {
const src = new Uint8Array(memory.buffer, srcPtr, chunk.length);
src.set(chunk);
ins.exports._inflate(this.zstreamPtr, srcPtr, dstPtr, chunk.length, CHUNK_SIZE);
}
onData(chunk) {
if (this.buff.length < this.offset + chunk.length) {
const buff = this.buff;
this.buff = new Uint8Array(this.buff.length * 2);
this.buff.set(buff);
}
this.buff.set(chunk, this.offset);
this.offset += chunk.length;
}
destroy() {
ins.exports._freeInflateContext(this.zstreamPtr);
delete map[this.zstreamPtr];
this.buff = null;
}
getBuffer() {
const res = new Uint8Array(this.offset);
for (let i = 0; i < this.offset; ++i) {
res[i] = this.buff[i];
}
return res;
}
}
ret = {
inflate(rawDeflateBuffer) {
const rawInf = new RawInf();
for (let offset = 0; offset < rawDeflateBuffer.length; offset += CHUNK_SIZE) {
const end = Math.min(offset + CHUNK_SIZE, rawDeflateBuffer.length);
const chunk = rawDeflateBuffer.subarray(offset, end);
rawInf.inflate(chunk);
}
const ret = rawInf.getBuffer();
rawInf.destroy();
return ret;
},
deflate(rawInflateBuffer) {
const rawDef = new RawDef();
for (let offset = 0; offset < rawInflateBuffer.length; offset += CHUNK_SIZE) {
const end = Math.min(offset + CHUNK_SIZE, rawInflateBuffer.length);
const chunk = rawInflateBuffer.subarray(offset, end);
rawDef.deflate(chunk, rawInflateBuffer.length <= offset + CHUNK_SIZE);
}
const ret = rawDef.getBuffer();
rawDef.destroy();
return ret;
},
}
return ret;
}
global.zlib = initialize();

BIN
js/zlib-1.2.11.wasm Normal file

Binary file not shown.

View file

@ -55,7 +55,7 @@ if ($ZEROBINCOMPATIBILITY):
<?php <?php
endif; endif;
?> ?>
<script type="text/javascript" data-cfasync="false" src="js/rawdeflate-0.5.js" integrity="sha512-tTdZ7qMr7tt5VQy4iCHu6/aGB12eRwbUy+AEI5rXntfsjcRfBeeqJloMsBU9FrGk1bIYLiuND/FhU42LO1bi0g==" crossorigin="anonymous"></script> <script type="text/javascript" data-cfasync="false" src="js/zlib-1.2.11.js" integrity="sha512-UOOlXbjGGS/ezf/cbE3Hc4L0ybB2NQBbk523tAT8m1TsnagkIgYUKuFfl+DL4wZbu7IopLvhI2kl+61+2oaaGA==" crossorigin="anonymous"></script>
<script type="text/javascript" data-cfasync="false" src="js/rawinflate-0.3.js" integrity="sha512-g8uelGgJW9A/Z1tB6Izxab++oj5kdD7B4qC7DHwZkB6DGMXKyzx7v5mvap2HXueI2IIn08YlRYM56jwWdm2ucQ==" crossorigin="anonymous"></script> <script type="text/javascript" data-cfasync="false" src="js/rawinflate-0.3.js" integrity="sha512-g8uelGgJW9A/Z1tB6Izxab++oj5kdD7B4qC7DHwZkB6DGMXKyzx7v5mvap2HXueI2IIn08YlRYM56jwWdm2ucQ==" crossorigin="anonymous"></script>
<script type="text/javascript" data-cfasync="false" src="js/bootstrap-3.3.7.js" integrity="sha512-iztkobsvnjKfAtTNdHkGVjAYTrrtlC7mGp/54c40wowO7LhURYl3gVzzcEqGl/qKXQltJ2HwMrdLcNUdo+N/RQ==" crossorigin="anonymous"></script> <script type="text/javascript" data-cfasync="false" src="js/bootstrap-3.3.7.js" integrity="sha512-iztkobsvnjKfAtTNdHkGVjAYTrrtlC7mGp/54c40wowO7LhURYl3gVzzcEqGl/qKXQltJ2HwMrdLcNUdo+N/RQ==" crossorigin="anonymous"></script>
<?php <?php
@ -71,7 +71,7 @@ if ($MARKDOWN):
endif; endif;
?> ?>
<script type="text/javascript" data-cfasync="false" src="js/purify-1.0.7.js" integrity="sha512-VnKJHLosO8z2ojNvWk9BEKYqnhZyWK9rM90FgZUUEp/PRnUqR5OLLKE0a3BkVmn7YgB7LXRrjHgFHQYKd6DAIA==" crossorigin="anonymous"></script> <script type="text/javascript" data-cfasync="false" src="js/purify-1.0.7.js" integrity="sha512-VnKJHLosO8z2ojNvWk9BEKYqnhZyWK9rM90FgZUUEp/PRnUqR5OLLKE0a3BkVmn7YgB7LXRrjHgFHQYKd6DAIA==" crossorigin="anonymous"></script>
<script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-cA9eDF/cppVcDihgEmTRmd2NGcSmT3XQnvO5uGQ6+rkr2VRIyIq4oHlBUvPGIBaJ3Fr40ovgUFeIJOIDyA/gXg==" crossorigin="anonymous"></script> <script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-i5UkiqPD3iKG9PN5jspS3LhMrJQz5HLDEqXuPrVTr/yKY5FpGbhY5hZ72YLU6ixElnZpW7gPDnkf8GmLSc/N4Q==" crossorigin="anonymous"></script>
<!--[if lt IE 10]> <!--[if lt IE 10]>
<style type="text/css">body {padding-left:60px;padding-right:60px;} #ienotice {display:block;} #oldienotice {display:block;}</style> <style type="text/css">body {padding-left:60px;padding-right:60px;} #ienotice {display:block;} #oldienotice {display:block;}</style>
<![endif]--> <![endif]-->

View file

@ -34,7 +34,7 @@ if ($ZEROBINCOMPATIBILITY):
<?php <?php
endif; endif;
?> ?>
<script type="text/javascript" data-cfasync="false" src="js/rawdeflate-0.5.js" integrity="sha512-tTdZ7qMr7tt5VQy4iCHu6/aGB12eRwbUy+AEI5rXntfsjcRfBeeqJloMsBU9FrGk1bIYLiuND/FhU42LO1bi0g==" crossorigin="anonymous"></script> <script type="text/javascript" data-cfasync="false" src="js/zlib-1.2.11.js" integrity="sha512-UOOlXbjGGS/ezf/cbE3Hc4L0ybB2NQBbk523tAT8m1TsnagkIgYUKuFfl+DL4wZbu7IopLvhI2kl+61+2oaaGA==" crossorigin="anonymous"></script>
<script type="text/javascript" data-cfasync="false" src="js/rawinflate-0.3.js" integrity="sha512-g8uelGgJW9A/Z1tB6Izxab++oj5kdD7B4qC7DHwZkB6DGMXKyzx7v5mvap2HXueI2IIn08YlRYM56jwWdm2ucQ==" crossorigin="anonymous"></script> <script type="text/javascript" data-cfasync="false" src="js/rawinflate-0.3.js" integrity="sha512-g8uelGgJW9A/Z1tB6Izxab++oj5kdD7B4qC7DHwZkB6DGMXKyzx7v5mvap2HXueI2IIn08YlRYM56jwWdm2ucQ==" crossorigin="anonymous"></script>
<?php <?php
if ($SYNTAXHIGHLIGHTING): if ($SYNTAXHIGHLIGHTING):
@ -49,7 +49,7 @@ if ($MARKDOWN):
endif; endif;
?> ?>
<script type="text/javascript" data-cfasync="false" src="js/purify-1.0.7.js" integrity="sha512-VnKJHLosO8z2ojNvWk9BEKYqnhZyWK9rM90FgZUUEp/PRnUqR5OLLKE0a3BkVmn7YgB7LXRrjHgFHQYKd6DAIA==" crossorigin="anonymous"></script> <script type="text/javascript" data-cfasync="false" src="js/purify-1.0.7.js" integrity="sha512-VnKJHLosO8z2ojNvWk9BEKYqnhZyWK9rM90FgZUUEp/PRnUqR5OLLKE0a3BkVmn7YgB7LXRrjHgFHQYKd6DAIA==" crossorigin="anonymous"></script>
<script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-cA9eDF/cppVcDihgEmTRmd2NGcSmT3XQnvO5uGQ6+rkr2VRIyIq4oHlBUvPGIBaJ3Fr40ovgUFeIJOIDyA/gXg==" crossorigin="anonymous"></script> <script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-i5UkiqPD3iKG9PN5jspS3LhMrJQz5HLDEqXuPrVTr/yKY5FpGbhY5hZ72YLU6ixElnZpW7gPDnkf8GmLSc/N4Q==" crossorigin="anonymous"></script>
<!--[if lt IE 10]> <!--[if lt IE 10]>
<style type="text/css">body {padding-left:60px;padding-right:60px;} #ienotice {display:block;} #oldienotice {display:block;}</style> <style type="text/css">body {padding-left:60px;padding-right:60px;} #ienotice {display:block;} #oldienotice {display:block;}</style>
<![endif]--> <![endif]-->