mirror of
https://github.com/PrivateBin/PrivateBin.git
synced 2024-10-01 01:26:10 -04:00
initial refactoring for support of version 2 paste format, some cleanup on the side
This commit is contained in:
parent
f3165f0cab
commit
0ab06e34ec
458
js/privatebin.js
458
js/privatebin.js
@ -701,17 +701,25 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||||||
* @private
|
* @private
|
||||||
* @param {string} key
|
* @param {string} key
|
||||||
* @param {string} password
|
* @param {string} password
|
||||||
* @param {object} object cryptographic message
|
* @param {array} spec cryptographic specification
|
||||||
* @return {CryptoKey} derived key
|
* @return {CryptoKey} derived key
|
||||||
*/
|
*/
|
||||||
async function deriveKey(key, password, object)
|
async function deriveKey(key, password, spec)
|
||||||
{
|
{
|
||||||
let keyArray = StrToArr(key);
|
let keyArray = StrToArr(key);
|
||||||
if ((password || '').trim().length > 0) {
|
if ((password || '').trim().length > 0) {
|
||||||
keyArray += await window.crypto.subtle.digest(
|
let passwordBuffer = await window.crypto.subtle.digest(
|
||||||
{name: 'SHA-256'},
|
{name: 'SHA-256'},
|
||||||
StrToArr(password)
|
StrToArr(utob(password))
|
||||||
);
|
);
|
||||||
|
let hexHash = Array.prototype.map.call(
|
||||||
|
new Uint8Array(passwordBuffer), x => ('00' + x.toString(16)).slice(-2)
|
||||||
|
).join('');
|
||||||
|
let passwordArray = StrToArr(hexHash),
|
||||||
|
newKeyArray = new Uint8Array(keyArray.length + passwordArray.length);
|
||||||
|
newKeyArray.set(keyArray, 0);
|
||||||
|
newKeyArray.set(passwordArray, keyArray.length);
|
||||||
|
keyArray = newKeyArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
// import raw key
|
// import raw key
|
||||||
@ -724,39 +732,40 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// derive a stronger key for use with AES
|
// derive a stronger key for use with AES
|
||||||
return await window.crypto.subtle.deriveKey(
|
return window.crypto.subtle.deriveKey(
|
||||||
{
|
{
|
||||||
name: 'PBKDF2', // we use PBKDF2 for key derivation
|
name: 'PBKDF2', // we use PBKDF2 for key derivation
|
||||||
salt: StrToArr(atob(object.salt)), // salt used in HMAC
|
salt: StrToArr(spec[1]), // salt used in HMAC
|
||||||
iterations: object.iter, // amount of iterations to apply
|
iterations: spec[2], // amount of iterations to apply
|
||||||
hash: {name: 'SHA-256'} // can be "SHA-1", "SHA-256", "SHA-384" or "SHA-512"
|
hash: {name: 'SHA-256'} // can be "SHA-1", "SHA-256", "SHA-384" or "SHA-512"
|
||||||
},
|
},
|
||||||
importedKey,
|
importedKey,
|
||||||
{
|
{
|
||||||
name: 'AES-' + object.mode.toUpperCase(), // can be any supported AES algorithm ("AES-CTR", "AES-CBC", "AES-CMAC", "AES-GCM", "AES-CFB", "AES-KW", "ECDH", "DH" or "HMAC")
|
name: 'AES-' + spec[6].toUpperCase(), // can be any supported AES algorithm ("AES-CTR", "AES-CBC", "AES-CMAC", "AES-GCM", "AES-CFB", "AES-KW", "ECDH", "DH" or "HMAC")
|
||||||
length: object.ks // can be 128, 192 or 256
|
length: spec[3] // can be 128, 192 or 256
|
||||||
},
|
},
|
||||||
false, // the key may not be exported
|
false, // the key may not be exported
|
||||||
['encrypt'] // we may only use it for decryption
|
['encrypt', 'decrypt'] // we use it for de- and encryption
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* gets crypto settings from given object
|
* gets crypto settings from specification and authenticated data
|
||||||
*
|
*
|
||||||
* @name CryptTool.cryptoSettings
|
* @name CryptTool.cryptoSettings
|
||||||
* @function
|
* @function
|
||||||
* @private
|
* @private
|
||||||
* @param {object} object cryptographic message
|
* @param {string} adata authenticated data
|
||||||
|
* @param {array} spec cryptographic specification
|
||||||
* @return {object} crypto settings
|
* @return {object} crypto settings
|
||||||
*/
|
*/
|
||||||
function cryptoSettings(object)
|
function cryptoSettings(adata, spec)
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
name: 'AES-' + object.mode.toUpperCase(), // can be any supported AES algorithm ("AES-CTR", "AES-CBC", "AES-CMAC", "AES-GCM", "AES-CFB", "AES-KW", "ECDH", "DH" or "HMAC")
|
name: 'AES-' + spec[6].toUpperCase(), // can be any supported AES algorithm ("AES-CTR", "AES-CBC", "AES-CMAC", "AES-GCM", "AES-CFB", "AES-KW", "ECDH", "DH" or "HMAC")
|
||||||
iv: StrToArr(atob(object.iv)), // the initialization vector you used to encrypt
|
iv: StrToArr(spec[0]), // the initialization vector you used to encrypt
|
||||||
additionalData: StrToArr(atob(object.adata)), // the addtional data you used during encryption (if any)
|
additionalData: StrToArr(adata), // the addtional data you used during encryption (if any)
|
||||||
tagLength: object.ts // the length of the tag you used to encrypt (if any)
|
tagLength: spec[4] // the length of the tag you used to encrypt (if any)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -769,32 +778,53 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||||||
* @param {string} key
|
* @param {string} key
|
||||||
* @param {string} password
|
* @param {string} password
|
||||||
* @param {string} message
|
* @param {string} message
|
||||||
* @return {string} data - JSON with encrypted data
|
* @param {array} adata
|
||||||
|
* @return {array} encrypted message & adata containing encryption spec
|
||||||
*/
|
*/
|
||||||
me.cipher = async function(key, password, message)
|
me.cipher = async function(key, password, message, adata)
|
||||||
{
|
{
|
||||||
// AES in Galois Counter Mode, keysize 256 bit, authentication tag 128 bit, 10000 iterations in key derivation
|
// AES in Galois Counter Mode, keysize 256 bit,
|
||||||
const iv = getRandomBytes(16);
|
// authentication tag 128 bit, 10000 iterations in key derivation
|
||||||
let object = {
|
const spec = [
|
||||||
iv: btoa(iv),
|
getRandomBytes(16), // initialization vector
|
||||||
v: 1,
|
getRandomBytes(8), // salt
|
||||||
iter: 10000,
|
10000, // iterations
|
||||||
ks: 256,
|
256, // key size
|
||||||
ts: 128,
|
128, // tag size
|
||||||
mode: 'gcm',
|
'aes', // algorithm
|
||||||
adata: '', // if used, base64 encode it with btoa()
|
'gcm', // algorithm mode
|
||||||
cipher: 'aes',
|
'none' // compression
|
||||||
salt: btoa(getRandomBytes(8))
|
], encodedSpec = [
|
||||||
};
|
btoa(spec[0]),
|
||||||
|
btoa(spec[1]),
|
||||||
|
spec[2],
|
||||||
|
spec[3],
|
||||||
|
spec[4],
|
||||||
|
spec[5],
|
||||||
|
spec[6],
|
||||||
|
spec[7]
|
||||||
|
];
|
||||||
|
if (adata.length === 0) {
|
||||||
|
// comment
|
||||||
|
adata = encodedSpec;
|
||||||
|
} else if (adata[0] === null) {
|
||||||
|
// paste
|
||||||
|
adata[0] = encodedSpec;
|
||||||
|
}
|
||||||
|
|
||||||
// finally, encrypt message
|
// finally, encrypt message
|
||||||
const encrypted = await window.crypto.subtle.encrypt(
|
return [
|
||||||
cryptoSettings(object),
|
btoa(
|
||||||
await deriveKey(key, password, object),
|
ArrToStr(
|
||||||
StrToArr(compress(message)) // compressed plain text to encrypt
|
await window.crypto.subtle.encrypt(
|
||||||
);
|
cryptoSettings(JSON.stringify(adata), spec),
|
||||||
object.ct = btoa(ArrToStr(encrypted));
|
await deriveKey(key, password, spec),
|
||||||
return JSON.stringify(object);
|
StrToArr(utob(message))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
adata
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -805,25 +835,57 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||||||
* @function
|
* @function
|
||||||
* @param {string} key
|
* @param {string} key
|
||||||
* @param {string} password
|
* @param {string} password
|
||||||
* @param {string} data - JSON with encrypted data
|
* @param {string|object} data encrypted message
|
||||||
* @return {string} decrypted message, empty if decryption failed
|
* @return {string} decrypted message, empty if decryption failed
|
||||||
*/
|
*/
|
||||||
me.decipher = async function(key, password, data)
|
me.decipher = async function(key, password, data)
|
||||||
{
|
{
|
||||||
|
let adataString, encodedSpec, compression, cipherMessage;
|
||||||
|
if (data instanceof Array) {
|
||||||
|
// version 2
|
||||||
|
adataString = JSON.stringify(data[1]);
|
||||||
|
encodedSpec = (data[1][0] instanceof Array ? data[1][0] : data[1]);
|
||||||
|
cipherMessage = data[0];
|
||||||
|
} else if (typeof data === 'string') {
|
||||||
|
// version 1
|
||||||
|
let object = JSON.parse(data);
|
||||||
|
adataString = atob(object.adata);
|
||||||
|
encodedSpec = [
|
||||||
|
object.iv,
|
||||||
|
object.salt,
|
||||||
|
object.iter,
|
||||||
|
object.ks,
|
||||||
|
object.ts,
|
||||||
|
object.cipher,
|
||||||
|
object.mode,
|
||||||
|
'rawdeflate'
|
||||||
|
];
|
||||||
|
cipherMessage = object.ct;
|
||||||
|
} else {
|
||||||
|
throw 'unsupported message format';
|
||||||
|
}
|
||||||
|
compression = encodedSpec[7];
|
||||||
|
let spec = encodedSpec, plainText = '';
|
||||||
|
spec[0] = atob(spec[0]);
|
||||||
|
spec[1] = atob(spec[1]);
|
||||||
try {
|
try {
|
||||||
const object = JSON.parse(data);
|
plainText = ArrToStr(
|
||||||
return decompress(
|
|
||||||
ArrToStr(
|
|
||||||
await window.crypto.subtle.decrypt(
|
await window.crypto.subtle.decrypt(
|
||||||
cryptoSettings(object),
|
cryptoSettings(adataString, spec),
|
||||||
await deriveKey(key, password, object),
|
await deriveKey(key, password, spec),
|
||||||
StrToArr(atob(object.ct)) // cipher text to decrypt
|
StrToArr(atob(cipherMessage))
|
||||||
)
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
if (compression === 'none') {
|
||||||
|
return btou(plainText);
|
||||||
|
} else if (compression === 'rawdeflate') {
|
||||||
|
return decompress(plainText);
|
||||||
|
} else {
|
||||||
|
throw 'unsupported compression format';
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -906,25 +968,25 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// reload data
|
// reload data
|
||||||
Uploader.prepare();
|
ServerInteraction.prepare();
|
||||||
Uploader.setUrl(Helper.baseUri() + '?' + me.getPasteId());
|
ServerInteraction.setUrl(Helper.baseUri() + '?' + me.getPasteId());
|
||||||
|
|
||||||
Uploader.setFailure(function (status, data) {
|
ServerInteraction.setFailure(function (status, data) {
|
||||||
// revert loading status…
|
// revert loading status…
|
||||||
Alert.hideLoading();
|
Alert.hideLoading();
|
||||||
TopNav.showViewButtons();
|
TopNav.showViewButtons();
|
||||||
|
|
||||||
// show error message
|
// show error message
|
||||||
Alert.showError(Uploader.parseUploadError(status, data, 'get paste data'));
|
Alert.showError(ServerInteraction.parseUploadError(status, data, 'get paste data'));
|
||||||
});
|
});
|
||||||
Uploader.setSuccess(function (status, data) {
|
ServerInteraction.setSuccess(function (status, data) {
|
||||||
pasteData = data;
|
pasteData = data;
|
||||||
|
|
||||||
if (typeof callback === 'function') {
|
if (typeof callback === 'function') {
|
||||||
return callback(data);
|
return callback(data);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Uploader.run();
|
ServerInteraction.run();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1290,7 +1352,6 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||||||
*/
|
*/
|
||||||
me.showStatus = function(message, icon)
|
me.showStatus = function(message, icon)
|
||||||
{
|
{
|
||||||
console.info('status shown: ', message);
|
|
||||||
handleNotification(1, $statusMessage, message, icon);
|
handleNotification(1, $statusMessage, message, icon);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1307,7 +1368,6 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||||||
*/
|
*/
|
||||||
me.showError = function(message, icon)
|
me.showError = function(message, icon)
|
||||||
{
|
{
|
||||||
console.error('error message shown: ', message);
|
|
||||||
handleNotification(3, $errorMessage, message, icon);
|
handleNotification(3, $errorMessage, message, icon);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1322,7 +1382,6 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||||||
*/
|
*/
|
||||||
me.showRemaining = function(message)
|
me.showRemaining = function(message)
|
||||||
{
|
{
|
||||||
console.info('remaining message shown: ', message);
|
|
||||||
handleNotification(1, $remainingTime, message);
|
handleNotification(1, $remainingTime, message);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1338,10 +1397,6 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||||||
*/
|
*/
|
||||||
me.showLoading = function(message, icon)
|
me.showLoading = function(message, icon)
|
||||||
{
|
{
|
||||||
if (typeof message !== 'undefined' && message !== null) {
|
|
||||||
console.info('status changed: ', message);
|
|
||||||
}
|
|
||||||
|
|
||||||
// default message text
|
// default message text
|
||||||
if (typeof message === 'undefined') {
|
if (typeof message === 'undefined') {
|
||||||
message = 'Loading…';
|
message = 'Loading…';
|
||||||
@ -2132,7 +2187,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||||||
me.hide = function()
|
me.hide = function()
|
||||||
{
|
{
|
||||||
if (!isDisplayed) {
|
if (!isDisplayed) {
|
||||||
console.warn('PasteViewer was called to hide the parsed view, but it is already hidden.');
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$plainText.addClass('hidden');
|
$plainText.addClass('hidden');
|
||||||
@ -3184,7 +3239,6 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||||||
me.showViewButtons = function()
|
me.showViewButtons = function()
|
||||||
{
|
{
|
||||||
if (viewButtonsDisplayed) {
|
if (viewButtonsDisplayed) {
|
||||||
console.warn('showViewButtons: view buttons are already displayed');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3205,7 +3259,6 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||||||
me.hideViewButtons = function()
|
me.hideViewButtons = function()
|
||||||
{
|
{
|
||||||
if (!viewButtonsDisplayed) {
|
if (!viewButtonsDisplayed) {
|
||||||
console.warn('hideViewButtons: view buttons are already hidden');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3238,7 +3291,6 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||||||
me.showCreateButtons = function()
|
me.showCreateButtons = function()
|
||||||
{
|
{
|
||||||
if (createButtonsDisplayed) {
|
if (createButtonsDisplayed) {
|
||||||
console.warn('showCreateButtons: create buttons are already displayed');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3263,7 +3315,6 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||||||
me.hideCreateButtons = function()
|
me.hideCreateButtons = function()
|
||||||
{
|
{
|
||||||
if (!createButtonsDisplayed) {
|
if (!createButtonsDisplayed) {
|
||||||
console.warn('hideCreateButtons: create buttons are already hidden');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3534,23 +3585,23 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||||||
/**
|
/**
|
||||||
* Responsible for AJAX requests, transparently handles encryption…
|
* Responsible for AJAX requests, transparently handles encryption…
|
||||||
*
|
*
|
||||||
* @name Uploader
|
* @name ServerInteraction
|
||||||
* @class
|
* @class
|
||||||
*/
|
*/
|
||||||
var Uploader = (function () {
|
var ServerInteraction = (function () {
|
||||||
var me = {};
|
var me = {};
|
||||||
|
|
||||||
var successFunc = null,
|
var successFunc = null,
|
||||||
failureFunc = null,
|
failureFunc = null,
|
||||||
|
symmetricKey = null,
|
||||||
url,
|
url,
|
||||||
data,
|
data,
|
||||||
symmetricKey,
|
|
||||||
password;
|
password;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* public variable ('constant') for errors to prevent magic numbers
|
* public variable ('constant') for errors to prevent magic numbers
|
||||||
*
|
*
|
||||||
* @name Uploader.error
|
* @name ServerInteraction.error
|
||||||
* @readonly
|
* @readonly
|
||||||
* @enum {Object}
|
* @enum {Object}
|
||||||
*/
|
*/
|
||||||
@ -3564,7 +3615,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||||||
/**
|
/**
|
||||||
* ajaxHeaders to send in AJAX requests
|
* ajaxHeaders to send in AJAX requests
|
||||||
*
|
*
|
||||||
* @name Uploader.ajaxHeaders
|
* @name ServerInteraction.ajaxHeaders
|
||||||
* @private
|
* @private
|
||||||
* @readonly
|
* @readonly
|
||||||
* @enum {Object}
|
* @enum {Object}
|
||||||
@ -3574,40 +3625,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||||||
/**
|
/**
|
||||||
* called after successful upload
|
* called after successful upload
|
||||||
*
|
*
|
||||||
* @name Uploader.checkCryptParameters
|
* @name ServerInteraction.success
|
||||||
* @private
|
|
||||||
* @function
|
|
||||||
* @throws {string}
|
|
||||||
*/
|
|
||||||
function checkCryptParameters()
|
|
||||||
{
|
|
||||||
// workaround for this nasty 'bug' in ECMAScript
|
|
||||||
// see https://stackoverflow.com/questions/18808226/why-is-typeof-null-object
|
|
||||||
var typeOfKey = typeof symmetricKey;
|
|
||||||
if (symmetricKey === null) {
|
|
||||||
typeOfKey = 'null';
|
|
||||||
}
|
|
||||||
|
|
||||||
// in case of missing preparation, throw error
|
|
||||||
switch (typeOfKey) {
|
|
||||||
case 'string':
|
|
||||||
// already set, all right
|
|
||||||
return;
|
|
||||||
case 'null':
|
|
||||||
// needs to be generated auto-generate
|
|
||||||
symmetricKey = CryptTool.getSymmetricKey();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
console.error('current invalid symmetricKey: ', symmetricKey);
|
|
||||||
throw 'symmetricKey is invalid, probably the module was not prepared';
|
|
||||||
}
|
|
||||||
// password is optional
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* called after successful upload
|
|
||||||
*
|
|
||||||
* @name Uploader.success
|
|
||||||
* @private
|
* @private
|
||||||
* @function
|
* @function
|
||||||
* @param {int} status
|
* @param {int} status
|
||||||
@ -3627,7 +3645,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||||||
/**
|
/**
|
||||||
* called after a upload failure
|
* called after a upload failure
|
||||||
*
|
*
|
||||||
* @name Uploader.fail
|
* @name ServerInteraction.fail
|
||||||
* @private
|
* @private
|
||||||
* @function
|
* @function
|
||||||
* @param {int} status - internal code
|
* @param {int} status - internal code
|
||||||
@ -3643,13 +3661,13 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||||||
/**
|
/**
|
||||||
* actually uploads the data
|
* actually uploads the data
|
||||||
*
|
*
|
||||||
* @name Uploader.run
|
* @name ServerInteraction.run
|
||||||
* @function
|
* @function
|
||||||
*/
|
*/
|
||||||
me.run = function()
|
me.run = function()
|
||||||
{
|
{
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: 'POST',
|
type: data ? 'POST' : 'GET',
|
||||||
url: url,
|
url: url,
|
||||||
data: data,
|
data: data,
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
@ -3673,7 +3691,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||||||
/**
|
/**
|
||||||
* set success function
|
* set success function
|
||||||
*
|
*
|
||||||
* @name Uploader.setUrl
|
* @name ServerInteraction.setUrl
|
||||||
* @function
|
* @function
|
||||||
* @param {function} newUrl
|
* @param {function} newUrl
|
||||||
*/
|
*/
|
||||||
@ -3684,11 +3702,11 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* sets the password to use (first value) and optionally also the
|
* sets the password to use (first value) and optionally also the
|
||||||
* encryption key (not recommend, it is automatically generated).
|
* encryption key (not recommended, it is automatically generated).
|
||||||
*
|
*
|
||||||
* Note: Call this after prepare() as prepare() resets these values.
|
* Note: Call this after prepare() as prepare() resets these values.
|
||||||
*
|
*
|
||||||
* @name Uploader.setCryptValues
|
* @name ServerInteraction.setCryptValues
|
||||||
* @function
|
* @function
|
||||||
* @param {string} newPassword
|
* @param {string} newPassword
|
||||||
* @param {string} newKey - optional
|
* @param {string} newKey - optional
|
||||||
@ -3705,7 +3723,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||||||
/**
|
/**
|
||||||
* set success function
|
* set success function
|
||||||
*
|
*
|
||||||
* @name Uploader.setSuccess
|
* @name ServerInteraction.setSuccess
|
||||||
* @function
|
* @function
|
||||||
* @param {function} func
|
* @param {function} func
|
||||||
*/
|
*/
|
||||||
@ -3717,7 +3735,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||||||
/**
|
/**
|
||||||
* set failure function
|
* set failure function
|
||||||
*
|
*
|
||||||
* @name Uploader.setFailure
|
* @name ServerInteraction.setFailure
|
||||||
* @function
|
* @function
|
||||||
* @param {function} func
|
* @param {function} func
|
||||||
*/
|
*/
|
||||||
@ -3733,7 +3751,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||||||
* previous uploads. Must be called before any other method of this
|
* previous uploads. Must be called before any other method of this
|
||||||
* module.
|
* module.
|
||||||
*
|
*
|
||||||
* @name Uploader.prepare
|
* @name ServerInteraction.prepare
|
||||||
* @function
|
* @function
|
||||||
* @return {object}
|
* @return {object}
|
||||||
*/
|
*/
|
||||||
@ -3757,22 +3775,33 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||||||
/**
|
/**
|
||||||
* encrypts and sets the data
|
* encrypts and sets the data
|
||||||
*
|
*
|
||||||
* @name Uploader.setData
|
* @name ServerInteraction.setCipherMessage
|
||||||
* @async
|
* @async
|
||||||
* @function
|
* @function
|
||||||
* @param {string} index
|
* @param {object} cipherMessage
|
||||||
* @param {mixed} element
|
|
||||||
*/
|
*/
|
||||||
me.setData = async function(index, element)
|
me.setCipherMessage = async function(cipherMessage)
|
||||||
{
|
{
|
||||||
checkCryptParameters();
|
if (
|
||||||
data[index] = await CryptTool.cipher(symmetricKey, password, element);
|
symmetricKey === null ||
|
||||||
|
(typeof symmetricKey === 'string' && symmetricKey === '')
|
||||||
|
) {
|
||||||
|
symmetricKey = CryptTool.getSymmetricKey();
|
||||||
|
}
|
||||||
|
if (!data.hasOwnProperty('adata')) {
|
||||||
|
data['adata'] = [];
|
||||||
|
}
|
||||||
|
let cipherResult = await CryptTool.cipher(symmetricKey, password, JSON.stringify(cipherMessage), data['adata']);
|
||||||
|
data['v'] = 2;
|
||||||
|
data['ct'] = cipherResult[0];
|
||||||
|
data['adata'] = cipherResult[1];
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* set the additional metadata to send unencrypted
|
* set the additional metadata to send unencrypted
|
||||||
*
|
*
|
||||||
* @name Uploader.setUnencryptedData
|
* @name ServerInteraction.setUnencryptedData
|
||||||
* @function
|
* @function
|
||||||
* @param {string} index
|
* @param {string} index
|
||||||
* @param {mixed} element
|
* @param {mixed} element
|
||||||
@ -3783,21 +3812,9 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* set the additional metadata to send unencrypted passed at once
|
* Helper, which parses shows a general error message based on the result of the ServerInteraction
|
||||||
*
|
*
|
||||||
* @name Uploader.setUnencryptedData
|
* @name ServerInteraction.parseUploadError
|
||||||
* @function
|
|
||||||
* @param {object} newData
|
|
||||||
*/
|
|
||||||
me.setUnencryptedBulkData = function(newData)
|
|
||||||
{
|
|
||||||
$.extend(data, newData);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper, which parses shows a general error message based on the result of the Uploader
|
|
||||||
*
|
|
||||||
* @name Uploader.parseUploadError
|
|
||||||
* @function
|
* @function
|
||||||
* @param {int} status
|
* @param {int} status
|
||||||
* @param {object} data
|
* @param {object} data
|
||||||
@ -3825,24 +3842,13 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||||||
return errorArray;
|
return errorArray;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* init Uploader
|
|
||||||
*
|
|
||||||
* @name Uploader.init
|
|
||||||
* @function
|
|
||||||
*/
|
|
||||||
me.init = function()
|
|
||||||
{
|
|
||||||
// nothing yet
|
|
||||||
};
|
|
||||||
|
|
||||||
return me;
|
return me;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* (controller) Responsible for encrypting paste and sending it to server.
|
* (controller) Responsible for encrypting paste and sending it to server.
|
||||||
*
|
*
|
||||||
* Does upload, encryption is done transparently by Uploader.
|
* Does upload, encryption is done transparently by ServerInteraction.
|
||||||
*
|
*
|
||||||
* @name PasteEncrypter
|
* @name PasteEncrypter
|
||||||
* @class
|
* @class
|
||||||
@ -3906,43 +3912,6 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* adds attachments to the Uploader
|
|
||||||
*
|
|
||||||
* @name PasteEncrypter.encryptAttachments
|
|
||||||
* @private
|
|
||||||
* @function
|
|
||||||
* @param {function} callback - excuted when action is successful
|
|
||||||
*/
|
|
||||||
function encryptAttachments(callback) {
|
|
||||||
var file = AttachmentViewer.getAttachmentData();
|
|
||||||
|
|
||||||
let encryptAttachmentPromise, encryptAttachmentNamePromise;
|
|
||||||
if (typeof file !== 'undefined' && file !== null) {
|
|
||||||
var fileName = AttachmentViewer.getFile().name;
|
|
||||||
|
|
||||||
// run concurrently to encrypt everything
|
|
||||||
encryptAttachmentPromise = Uploader.setData('attachment', file);
|
|
||||||
encryptAttachmentNamePromise = Uploader.setData('attachmentname', fileName);
|
|
||||||
} else if (AttachmentViewer.hasAttachment()) {
|
|
||||||
// fall back to cloned part
|
|
||||||
var attachment = AttachmentViewer.getAttachment();
|
|
||||||
|
|
||||||
encryptAttachmentPromise = Uploader.setData('attachment', attachment[0]);
|
|
||||||
encryptAttachmentNamePromise = Uploader.setData('attachmentname', attachment[1]);
|
|
||||||
} else {
|
|
||||||
// if there are no attachments, this is of course still successful
|
|
||||||
return callback();
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: change this callback to also use Promises instead,
|
|
||||||
// this here just waits
|
|
||||||
return Promise.all([encryptAttachmentPromise, encryptAttachmentNamePromise]).then(() => {
|
|
||||||
// run callback
|
|
||||||
return callback();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* send a reply in a discussion
|
* send a reply in a discussion
|
||||||
*
|
*
|
||||||
@ -3973,20 +3942,20 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// prepare Uploader
|
// prepare server interaction
|
||||||
Uploader.prepare();
|
ServerInteraction.prepare();
|
||||||
Uploader.setCryptParameters(Prompt.getPassword(), Model.getPasteKey());
|
ServerInteraction.setCryptParameters(Prompt.getPassword(), Model.getPasteKey());
|
||||||
|
|
||||||
// set success/fail functions
|
// set success/fail functions
|
||||||
Uploader.setSuccess(showUploadedComment);
|
ServerInteraction.setSuccess(showUploadedComment);
|
||||||
Uploader.setFailure(function (status, data) {
|
ServerInteraction.setFailure(function (status, data) {
|
||||||
// revert loading status…
|
// revert loading status…
|
||||||
Alert.hideLoading();
|
Alert.hideLoading();
|
||||||
TopNav.showViewButtons();
|
TopNav.showViewButtons();
|
||||||
|
|
||||||
// …show error message…
|
// …show error message…
|
||||||
Alert.showError(
|
Alert.showError(
|
||||||
Uploader.parseUploadError(status, data, 'post comment')
|
ServerInteraction.parseUploadError(status, data, 'post comment')
|
||||||
);
|
);
|
||||||
|
|
||||||
// …and reset error handler
|
// …and reset error handler
|
||||||
@ -3994,28 +3963,24 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// fill it with unencrypted params
|
// fill it with unencrypted params
|
||||||
Uploader.setUnencryptedData('pasteid', Model.getPasteId());
|
ServerInteraction.setUnencryptedData('pasteid', Model.getPasteId());
|
||||||
if (typeof parentid === 'undefined') {
|
if (typeof parentid === 'undefined') {
|
||||||
// if parent id is not set, this is the top-most comment, so use
|
// if parent id is not set, this is the top-most comment, so use
|
||||||
// paste id as parent, as the root element of the discussion tree
|
// paste id as parent, as the root element of the discussion tree
|
||||||
Uploader.setUnencryptedData('parentid', Model.getPasteId());
|
ServerInteraction.setUnencryptedData('parentid', Model.getPasteId());
|
||||||
} else {
|
} else {
|
||||||
Uploader.setUnencryptedData('parentid', parentid);
|
ServerInteraction.setUnencryptedData('parentid', parentid);
|
||||||
}
|
}
|
||||||
|
|
||||||
// start promises to encrypt data…
|
// prepare cypher message
|
||||||
let dataPromises = [];
|
let cipherMessage = {
|
||||||
dataPromises.push(Uploader.setData('data', plainText));
|
'comment': plainText
|
||||||
|
};
|
||||||
if (nickname.length > 0) {
|
if (nickname.length > 0) {
|
||||||
dataPromises.push(Uploader.setData('nickname', nickname));
|
cipherMessage['nickname'] = nickname;
|
||||||
}
|
}
|
||||||
|
|
||||||
// …and upload when they are all done
|
await ServerInteraction.setCipherMessage(cipherMessage).catch(Alert.showError);
|
||||||
Promise.all(dataPromises).then(() => {
|
|
||||||
Uploader.run();
|
|
||||||
}).catch((e) => {
|
|
||||||
Alert.showError(e);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -4049,60 +4014,55 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// prepare Uploader
|
// prepare server interaction
|
||||||
Uploader.prepare();
|
ServerInteraction.prepare();
|
||||||
Uploader.setCryptParameters(TopNav.getPassword());
|
ServerInteraction.setCryptParameters(TopNav.getPassword());
|
||||||
|
|
||||||
// set success/fail functions
|
// set success/fail functions
|
||||||
Uploader.setSuccess(showCreatedPaste);
|
ServerInteraction.setSuccess(showCreatedPaste);
|
||||||
Uploader.setFailure(function (status, data) {
|
ServerInteraction.setFailure(function (status, data) {
|
||||||
// revert loading status…
|
// revert loading status…
|
||||||
Alert.hideLoading();
|
Alert.hideLoading();
|
||||||
TopNav.showCreateButtons();
|
TopNav.showCreateButtons();
|
||||||
|
|
||||||
// show error message
|
// show error message
|
||||||
Alert.showError(
|
Alert.showError(
|
||||||
Uploader.parseUploadError(status, data, 'create paste')
|
ServerInteraction.parseUploadError(status, data, 'create paste')
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
// fill it with unencrypted submitted options
|
// fill it with unencrypted submitted options
|
||||||
Uploader.setUnencryptedBulkData({
|
ServerInteraction.setUnencryptedData('adata', [
|
||||||
expire: TopNav.getExpiration(),
|
null, format,
|
||||||
formatter: format,
|
TopNav.getOpenDiscussion() ? 1 : 0,
|
||||||
burnafterreading: TopNav.getBurnAfterReading() ? 1 : 0,
|
TopNav.getBurnAfterReading() ? 1 : 0
|
||||||
opendiscussion: TopNav.getOpenDiscussion() ? 1 : 0
|
]);
|
||||||
});
|
ServerInteraction.setUnencryptedData('meta', {'expire': TopNav.getExpiration()});
|
||||||
|
|
||||||
// prepare PasteViewer for later preview
|
// prepare PasteViewer for later preview
|
||||||
PasteViewer.setText(plainText);
|
PasteViewer.setText(plainText);
|
||||||
PasteViewer.setFormat(format);
|
PasteViewer.setFormat(format);
|
||||||
|
|
||||||
// encrypt attachments
|
// prepare cypher message
|
||||||
const encryptAttachmentsPromise = encryptAttachments(
|
let file = AttachmentViewer.getAttachmentData(),
|
||||||
function () {
|
cipherMessage = {
|
||||||
// TODO: remove, is not needed anymore as we use Promises
|
'paste': plainText
|
||||||
|
};
|
||||||
|
if (typeof file !== 'undefined' && file !== null) {
|
||||||
|
cipherMessage['attachment'] = file;
|
||||||
|
cipherMessage['attachment_name'] = AttachmentViewer.getFile().name;
|
||||||
|
} else if (AttachmentViewer.hasAttachment()) {
|
||||||
|
// fall back to cloned part
|
||||||
|
let attachment = AttachmentViewer.getAttachment();
|
||||||
|
cipherMessage['attachment'] = attachment[0];
|
||||||
|
cipherMessage['attachment_name'] = attachment[1];
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
|
||||||
// encrypt plain text
|
// encrypt message
|
||||||
const encryptDataPromise = Uploader.setData('data', plainText);
|
await ServerInteraction.setCipherMessage(cipherMessage).catch(Alert.showError);
|
||||||
|
|
||||||
await Promise.all([encryptAttachmentsPromise, encryptDataPromise]).catch(Alert.showError);
|
|
||||||
|
|
||||||
// send data
|
// send data
|
||||||
Uploader.run();
|
ServerInteraction.run();
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* initialize
|
|
||||||
*
|
|
||||||
* @name PasteEncrypter.init
|
|
||||||
* @function
|
|
||||||
*/
|
|
||||||
me.init = function()
|
|
||||||
{
|
|
||||||
// nothing yet
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return me;
|
return me;
|
||||||
@ -4347,17 +4307,6 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* initialize
|
|
||||||
*
|
|
||||||
* @name PasteDecrypter.init
|
|
||||||
* @function
|
|
||||||
*/
|
|
||||||
me.init = function()
|
|
||||||
{
|
|
||||||
// nothing yet
|
|
||||||
};
|
|
||||||
|
|
||||||
return me;
|
return me;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
@ -4457,20 +4406,20 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||||||
var orgPosition = $(window).scrollTop();
|
var orgPosition = $(window).scrollTop();
|
||||||
|
|
||||||
Model.getPasteData(function (data) {
|
Model.getPasteData(function (data) {
|
||||||
Uploader.prepare();
|
ServerInteraction.prepare();
|
||||||
Uploader.setUrl(Helper.baseUri() + '?' + Model.getPasteId());
|
ServerInteraction.setUrl(Helper.baseUri() + '?' + Model.getPasteId());
|
||||||
|
|
||||||
Uploader.setFailure(function (status, data) {
|
ServerInteraction.setFailure(function (status, data) {
|
||||||
// revert loading status…
|
// revert loading status…
|
||||||
Alert.hideLoading();
|
Alert.hideLoading();
|
||||||
TopNav.showViewButtons();
|
TopNav.showViewButtons();
|
||||||
|
|
||||||
// show error message
|
// show error message
|
||||||
Alert.showError(
|
Alert.showError(
|
||||||
Uploader.parseUploadError(status, data, 'refresh display')
|
ServerInteraction.parseUploadError(status, data, 'refresh display')
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
Uploader.setSuccess(function (status, data) {
|
ServerInteraction.setSuccess(function (status, data) {
|
||||||
PasteDecrypter.run(data);
|
PasteDecrypter.run(data);
|
||||||
|
|
||||||
// restore position
|
// restore position
|
||||||
@ -4481,7 +4430,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||||||
// password being entered
|
// password being entered
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
Uploader.run();
|
ServerInteraction.run();
|
||||||
}, false); // this false is important as it circumvents the cache
|
}, false); // this false is important as it circumvents the cache
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4551,14 +4500,11 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||||||
AttachmentViewer.init();
|
AttachmentViewer.init();
|
||||||
DiscussionViewer.init();
|
DiscussionViewer.init();
|
||||||
Editor.init();
|
Editor.init();
|
||||||
PasteDecrypter.init();
|
|
||||||
PasteEncrypter.init();
|
|
||||||
PasteStatus.init();
|
PasteStatus.init();
|
||||||
PasteViewer.init();
|
PasteViewer.init();
|
||||||
Prompt.init();
|
Prompt.init();
|
||||||
TopNav.init();
|
TopNav.init();
|
||||||
UiHelper.init();
|
UiHelper.init();
|
||||||
Uploader.init();
|
|
||||||
|
|
||||||
// check whether existing paste needs to be shown
|
// check whether existing paste needs to be shown
|
||||||
try {
|
try {
|
||||||
@ -4602,7 +4548,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
|||||||
AttachmentViewer: AttachmentViewer,
|
AttachmentViewer: AttachmentViewer,
|
||||||
DiscussionViewer: DiscussionViewer,
|
DiscussionViewer: DiscussionViewer,
|
||||||
TopNav: TopNav,
|
TopNav: TopNav,
|
||||||
Uploader: Uploader,
|
ServerInteraction: ServerInteraction,
|
||||||
PasteEncrypter: PasteEncrypter,
|
PasteEncrypter: PasteEncrypter,
|
||||||
PasteDecrypter: PasteDecrypter,
|
PasteDecrypter: PasteDecrypter,
|
||||||
Controller: Controller
|
Controller: Controller
|
||||||
|
@ -2,6 +2,11 @@
|
|||||||
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 () {
|
||||||
this.timeout(30000);
|
this.timeout(30000);
|
||||||
it('can en- and decrypt any message', function () {
|
it('can en- and decrypt any message', function () {
|
||||||
@ -9,24 +14,22 @@ describe('CryptTool', function () {
|
|||||||
'string',
|
'string',
|
||||||
'string',
|
'string',
|
||||||
'string',
|
'string',
|
||||||
function (key, password, message) {
|
async function (key, password, message) {
|
||||||
var clean = jsdom();
|
// pause to let async functions conclude
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 300));
|
||||||
|
let clean = jsdom();
|
||||||
window.crypto = new WebCrypto();
|
window.crypto = new WebCrypto();
|
||||||
message = message.trim();
|
message = message.trim();
|
||||||
return $.PrivateBin.CryptTool.cipher(
|
let cipherMessage = await $.PrivateBin.CryptTool.cipher(
|
||||||
key, password, message
|
key, password, message, []
|
||||||
).then(function(ciphertext) {
|
),
|
||||||
$.PrivateBin.CryptTool.decipher(
|
plaintext = await $.PrivateBin.CryptTool.decipher(
|
||||||
key, password, ciphertext
|
key, password, cipherMessage
|
||||||
).then(function(plaintext) {
|
);
|
||||||
clean();
|
clean();
|
||||||
return message === plaintext;
|
return message === plaintext;
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
),
|
));
|
||||||
// reducing amount of checks as running 100 async ones causes issues for later test scripts
|
|
||||||
{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"
|
||||||
@ -35,7 +38,7 @@ describe('CryptTool', function () {
|
|||||||
'supports PrivateBin v1 ciphertext (SJCL & browser atob)',
|
'supports PrivateBin v1 ciphertext (SJCL & browser atob)',
|
||||||
function () {
|
function () {
|
||||||
delete global.Base64;
|
delete global.Base64;
|
||||||
var clean = jsdom();
|
let clean = jsdom();
|
||||||
window.crypto = new WebCrypto();
|
window.crypto = new WebCrypto();
|
||||||
|
|
||||||
// Of course you can easily decipher the following texts, if you like.
|
// Of course you can easily decipher the following texts, if you like.
|
||||||
@ -43,7 +46,7 @@ describe('CryptTool', function () {
|
|||||||
return $.PrivateBin.CryptTool.decipher(
|
return $.PrivateBin.CryptTool.decipher(
|
||||||
'6t2qsmLyfXIokNCL+3/yl15rfTUBQvm5SOnFPvNE7Q8=',
|
'6t2qsmLyfXIokNCL+3/yl15rfTUBQvm5SOnFPvNE7Q8=',
|
||||||
// -- "That's amazing. I've got the same combination on my luggage."
|
// -- "That's amazing. I've got the same combination on my luggage."
|
||||||
Array.apply(0, Array(6)).map(function(_,b) { return b + 1; }).join(''),
|
Array.apply(0, Array(6)).map((_,b) => b + 1).join(''),
|
||||||
'{"iv":"4HNFIl7eYbCh6HuShctTIA==","v":1,"iter":10000,"ks"' +
|
'{"iv":"4HNFIl7eYbCh6HuShctTIA==","v":1,"iter":10000,"ks"' +
|
||||||
':256,"ts":128,"mode":"gcm","adata":"","cipher":"aes","sa' +
|
':256,"ts":128,"mode":"gcm","adata":"","cipher":"aes","sa' +
|
||||||
'lt":"u0lQvePq6L0=","ct":"fGPUVrDyaVr1ZDGb+kqQ3CPEW8x4YKG' +
|
'lt":"u0lQvePq6L0=","ct":"fGPUVrDyaVr1ZDGb+kqQ3CPEW8x4YKG' +
|
||||||
@ -120,7 +123,7 @@ describe('CryptTool', function () {
|
|||||||
return $.PrivateBin.CryptTool.decipher(
|
return $.PrivateBin.CryptTool.decipher(
|
||||||
'6t2qsmLyfXIokNCL+3/yl15rfTUBQvm5SOnFPvNE7Q8=',
|
'6t2qsmLyfXIokNCL+3/yl15rfTUBQvm5SOnFPvNE7Q8=',
|
||||||
// -- "That's amazing. I've got the same combination on my luggage."
|
// -- "That's amazing. I've got the same combination on my luggage."
|
||||||
Array.apply(0, Array(6)).map(function(_,b) { return b + 1; }).join(''),
|
Array.apply(0, Array(6)).map((_,b) => b + 1).join(''),
|
||||||
'{"iv":"aTnR2qBL1CAmLX8FdWe3VA==","v":1,"iter":10000,"ks"' +
|
'{"iv":"aTnR2qBL1CAmLX8FdWe3VA==","v":1,"iter":10000,"ks"' +
|
||||||
':256,"ts":128,"mode":"gcm","adata":"","cipher":"aes","sa' +
|
':256,"ts":128,"mode":"gcm","adata":"","cipher":"aes","sa' +
|
||||||
'lt":"u0lQvePq6L0=","ct":"A3nBTvICZtYy6xqbIJE0c8Veored5lM' +
|
'lt":"u0lQvePq6L0=","ct":"A3nBTvICZtYy6xqbIJE0c8Veored5lM' +
|
||||||
|
@ -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-/hqrAlB/+OWfUg9D/0knhNkmUCzSJNqK2GIU3KBt/vhgfFiKGByOAzFYsyNxINu7c1pEwc/F/ZL5A/iF1rnK0Q==" crossorigin="anonymous"></script>
|
<script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-RVKB+q9cuKqmERwDv0KDUn1HAeNHCtlWS8Ww9+YeCKW842yyyCEBKw0gR8oS0XU3AXUsKTr3iiUJSJtqrdwm9w==" 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]-->
|
||||||
|
@ -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-/hqrAlB/+OWfUg9D/0knhNkmUCzSJNqK2GIU3KBt/vhgfFiKGByOAzFYsyNxINu7c1pEwc/F/ZL5A/iF1rnK0Q==" crossorigin="anonymous"></script>
|
<script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-RVKB+q9cuKqmERwDv0KDUn1HAeNHCtlWS8Ww9+YeCKW842yyyCEBKw0gR8oS0XU3AXUsKTr3iiUJSJtqrdwm9w==" 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]-->
|
||||||
|
Loading…
Reference in New Issue
Block a user