implementing web crypto API for encryption
This commit is contained in:
parent
bd6888687f
commit
0dbbb61d11
|
@ -10,13 +10,13 @@ php:
|
||||||
|
|
||||||
# as this is a php project, node.js v4 (for JS unit testing) isn't installed
|
# as this is a php project, node.js v4 (for JS unit testing) isn't installed
|
||||||
install:
|
install:
|
||||||
- if [ ! -d "$HOME/.nvm" ]; then mkdir -p $HOME/.nvm && curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | NVM_METHOD=script bash; fi
|
- if [ ! -d "$HOME/.nvm" ]; then mkdir -p $HOME/.nvm && curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | NVM_METHOD=script bash; fi
|
||||||
- source ~/.nvm/nvm.sh && nvm install 4
|
- source ~/.nvm/nvm.sh && nvm install --lts
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
- composer install -n
|
- composer install -n
|
||||||
- npm install -g mocha
|
- npm install -g mocha
|
||||||
- cd js && npm install jsverify jsdom@9 jsdom-global@2 mime-types
|
- cd js && npm install
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- mocha
|
- mocha
|
||||||
|
|
|
@ -3,13 +3,13 @@
|
||||||
"description": "PrivateBin is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted in the browser using 256 bit AES in Galois Counter mode (GCM).",
|
"description": "PrivateBin is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted in the browser using 256 bit AES in Galois Counter mode (GCM).",
|
||||||
"type": "project",
|
"type": "project",
|
||||||
"keywords": ["private", "secure", "end-to-end-encrypted", "e2e", "paste", "pastebin", "zero", "zero-knowledge", "encryption", "encrypted", "AES"],
|
"keywords": ["private", "secure", "end-to-end-encrypted", "e2e", "paste", "pastebin", "zero", "zero-knowledge", "encryption", "encrypted", "AES"],
|
||||||
"homepage": "https://github.com/PrivateBin",
|
"homepage": "https://privatebin.info/",
|
||||||
"license":"zlib-acknowledgement",
|
"license":"zlib-acknowledgement",
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/PrivateBin/PrivateBin/issues",
|
"issues": "https://github.com/PrivateBin/PrivateBin/issues",
|
||||||
"wiki": "https://github.com/PrivateBin/PrivateBin/wiki",
|
"wiki": "https://github.com/PrivateBin/PrivateBin/wiki",
|
||||||
"source": "https://github.com/PrivateBin/PrivateBin",
|
"source": "https://github.com/PrivateBin/PrivateBin",
|
||||||
"docs": "https://zerobin.dssr.ch/documentation/"
|
"docs": "https://privatebin.info/codedoc/"
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^5.4.0 || ^7.0",
|
"php": "^5.4.0 || ^7.0",
|
||||||
|
|
|
@ -6,6 +6,7 @@ global.jsc = require('jsverify');
|
||||||
global.jsdom = require('jsdom-global');
|
global.jsdom = require('jsdom-global');
|
||||||
global.cleanup = global.jsdom();
|
global.cleanup = global.jsdom();
|
||||||
global.fs = require('fs');
|
global.fs = require('fs');
|
||||||
|
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');
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
{
|
||||||
|
"name": "privatebin",
|
||||||
|
"version": "1.2.1",
|
||||||
|
"description": "PrivateBin is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted in the browser using 256 bit AES in Galois Counter mode (GCM).",
|
||||||
|
"main": "privatebin.js",
|
||||||
|
"directories": {
|
||||||
|
"test": "test"
|
||||||
|
},
|
||||||
|
"dependencies": {},
|
||||||
|
"devDependencies": {
|
||||||
|
"jsdom": "^9.12.0",
|
||||||
|
"jsdom-global": "^2.1.1",
|
||||||
|
"jsverify": "^0.8.3",
|
||||||
|
"mime-types": "^2.1.20",
|
||||||
|
"node-webcrypto-ossl": "^1.0.37"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": "mocha"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/PrivateBin/PrivateBin.git"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"private",
|
||||||
|
"secure",
|
||||||
|
"end-to-end-encrypted",
|
||||||
|
"e2e",
|
||||||
|
"paste",
|
||||||
|
"pastebin",
|
||||||
|
"zero",
|
||||||
|
"zero-knowledge",
|
||||||
|
"encryption",
|
||||||
|
"encrypted",
|
||||||
|
"AES"
|
||||||
|
],
|
||||||
|
"author": "",
|
||||||
|
"license": "zlib-acknowledgement",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/PrivateBin/PrivateBin/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://privatebin.info/"
|
||||||
|
}
|
266
js/privatebin.js
266
js/privatebin.js
|
@ -526,6 +526,30 @@ jQuery.PrivateBin = (function($, sjcl, RawDeflate) {
|
||||||
var CryptTool = (function () {
|
var CryptTool = (function () {
|
||||||
var me = {};
|
var me = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* convert UTF-8 string stored in a DOMString to a standard UTF-16 DOMString
|
||||||
|
*
|
||||||
|
* Iterates over the bytes of the message, converting them all hexadecimal
|
||||||
|
* percent encoded representations, then URI decodes them all
|
||||||
|
*
|
||||||
|
* @name CryptTool.btou
|
||||||
|
* @function
|
||||||
|
* @private
|
||||||
|
* @param {string} message UTF-8 string
|
||||||
|
* @return {string} UTF-16 string
|
||||||
|
*/
|
||||||
|
function btou(message)
|
||||||
|
{
|
||||||
|
return decodeURIComponent(
|
||||||
|
message.split('').map(
|
||||||
|
function(character)
|
||||||
|
{
|
||||||
|
return '%' + ('00' + character.charCodeAt(0).toString(16)).slice(-2);
|
||||||
|
}
|
||||||
|
).join('')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* convert DOMString (UTF-16) to a UTF-8 string stored in a DOMString
|
* convert DOMString (UTF-16) to a UTF-8 string stored in a DOMString
|
||||||
*
|
*
|
||||||
|
@ -550,27 +574,49 @@ jQuery.PrivateBin = (function($, sjcl, RawDeflate) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* convert UTF-8 string stored in a DOMString to a standard UTF-16 DOMString
|
* convert ArrayBuffer into a UTF-8 string
|
||||||
*
|
*
|
||||||
* Iterates over the bytes of the message, converting them all hexadecimal
|
* Iterates over the bytes of the array, catenating them into a string
|
||||||
* percent encoded representations, then URI decodes them all
|
|
||||||
*
|
*
|
||||||
* @name CryptTool.btou
|
* @name CryptTool.ArrToStr
|
||||||
|
* @function
|
||||||
|
* @private
|
||||||
|
* @param {ArrayBuffer} messageArray
|
||||||
|
* @return {string} message
|
||||||
|
*/
|
||||||
|
function ArrToStr(messageArray)
|
||||||
|
{
|
||||||
|
var array = new Uint8Array(messageArray),
|
||||||
|
len = array.length,
|
||||||
|
message = '',
|
||||||
|
i = 0;
|
||||||
|
while(i < len) {
|
||||||
|
var c = array[i++];
|
||||||
|
message += String.fromCharCode(c);
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* convert UTF-8 string into a Uint8Array
|
||||||
|
*
|
||||||
|
* Iterates over the bytes of the message, writing them to the array
|
||||||
|
*
|
||||||
|
* @name CryptTool.StrToArr
|
||||||
* @function
|
* @function
|
||||||
* @private
|
* @private
|
||||||
* @param {string} message UTF-8 string
|
* @param {string} message UTF-8 string
|
||||||
* @return {string} UTF-16 string
|
* @return {Uint8Array} array
|
||||||
*/
|
*/
|
||||||
function btou(message)
|
function StrToArr(message)
|
||||||
{
|
{
|
||||||
return decodeURIComponent(
|
var messageUtf8 = message,
|
||||||
message.split('').map(
|
messageLen = messageUtf8.length,
|
||||||
function(character)
|
messageArray = new Uint8Array(messageLen);
|
||||||
{
|
for (var i = 0; i < messageLen; ++i) {
|
||||||
return '%' + ('00' + character.charCodeAt(0).toString(16)).slice(-2);
|
messageArray[i] = messageUtf8.charCodeAt(i);
|
||||||
}
|
}
|
||||||
).join('')
|
return messageArray;
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -611,6 +657,42 @@ jQuery.PrivateBin = (function($, sjcl, RawDeflate) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns specified number of random bytes
|
||||||
|
*
|
||||||
|
* @name CryptTool.getRandomBytes
|
||||||
|
* @function
|
||||||
|
* @private
|
||||||
|
* @param {int} length number of random bytes to fetch
|
||||||
|
* @throws {string}
|
||||||
|
* @return {string} random bytes
|
||||||
|
*/
|
||||||
|
function getRandomBytes(length)
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
typeof window !== 'undefined' &&
|
||||||
|
typeof Uint8Array !== 'undefined' &&
|
||||||
|
String.fromCodePoint &&
|
||||||
|
(
|
||||||
|
typeof window.crypto !== 'undefined' ||
|
||||||
|
typeof window.msCrypto !== 'undefined'
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
// modern browser environment
|
||||||
|
var bytes = '',
|
||||||
|
byteArray = new Uint8Array(length),
|
||||||
|
crypto = window.crypto || window.msCrypto;
|
||||||
|
crypto.getRandomValues(byteArray);
|
||||||
|
for (var i = 0; i < length; ++i) {
|
||||||
|
bytes += String.fromCharCode(byteArray[i]);
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
} else {
|
||||||
|
// legacy browser or unsupported environment
|
||||||
|
throw 'No supported crypto API detected, you may read pastes and comments, but can\'t create pastes or add new comments.';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* compress, then encrypt message with given key and password
|
* compress, then encrypt message with given key and password
|
||||||
*
|
*
|
||||||
|
@ -621,19 +703,68 @@ jQuery.PrivateBin = (function($, sjcl, RawDeflate) {
|
||||||
* @param {string} message
|
* @param {string} message
|
||||||
* @return {string} data - JSON with encrypted data
|
* @return {string} data - JSON with encrypted data
|
||||||
*/
|
*/
|
||||||
me.cipher = function(key, password, message)
|
me.cipher = async function(key, password, message)
|
||||||
{
|
{
|
||||||
// Galois Counter Mode, keysize 256 bit, authentication tag 128 bit
|
// AES in Galois Counter Mode, keysize 256 bit, authentication tag 128 bit, 10000 iterations in key derivation
|
||||||
var options = {
|
var iv = getRandomBytes(16),
|
||||||
mode: 'gcm',
|
salt = getRandomBytes(8),
|
||||||
ks: 256,
|
object = {
|
||||||
ts: 128
|
iv: btoa(iv),
|
||||||
};
|
v: 1,
|
||||||
|
iter: 10000,
|
||||||
|
ks: 256,
|
||||||
|
ts: 128,
|
||||||
|
mode: 'gcm',
|
||||||
|
adata: '', // if used, base64 encode it with btoa()
|
||||||
|
cipher: 'aes',
|
||||||
|
salt: btoa(salt)
|
||||||
|
},
|
||||||
|
algo = 'AES-' + object.mode.toUpperCase();
|
||||||
|
|
||||||
if ((password || '').trim().length === 0) {
|
if ((password || '').trim().length > 0) {
|
||||||
return sjcl.encrypt(key, compress(message), options);
|
key += sjcl.codec.hex.fromBits(sjcl.hash.sha256.hash(password));
|
||||||
}
|
}
|
||||||
return sjcl.encrypt(key + sjcl.codec.hex.fromBits(sjcl.hash.sha256.hash(password)), compress(message), options);
|
|
||||||
|
// import raw key
|
||||||
|
var importedKey = await window.crypto.subtle.importKey(
|
||||||
|
'raw', // only 'raw' is allowed
|
||||||
|
StrToArr(key),
|
||||||
|
{name: 'PBKDF2'}, // we use PBKDF2 for key derivation
|
||||||
|
false, // the key may not be exported
|
||||||
|
["deriveKey"] // we may only use it for key derivation
|
||||||
|
)
|
||||||
|
|
||||||
|
// derive a stronger key for use with AES
|
||||||
|
var derivedKey = await window.crypto.subtle.deriveKey(
|
||||||
|
{
|
||||||
|
name: 'PBKDF2', // we use PBKDF2 for key derivation
|
||||||
|
salt: StrToArr(atob(object.salt)), // salt used in HMAC
|
||||||
|
iterations: object.iter, // amount of iterations to apply
|
||||||
|
hash: {name: "SHA-256"}, // can be "SHA-1", "SHA-256", "SHA-384" or "SHA-512"
|
||||||
|
},
|
||||||
|
importedKey,
|
||||||
|
{
|
||||||
|
// can be any supported AES algorithm ("AES-CTR", "AES-CBC", "AES-CMAC", "AES-GCM", "AES-CFB", "AES-KW", "ECDH", "DH" or "HMAC")
|
||||||
|
name: algo,
|
||||||
|
length: object.ks, // can be 128, 192 or 256
|
||||||
|
},
|
||||||
|
false, // the key may not be exported
|
||||||
|
["encrypt"] // we may only use it for decryption
|
||||||
|
)
|
||||||
|
|
||||||
|
// finally, encrypt message
|
||||||
|
var encrypted = await window.crypto.subtle.encrypt(
|
||||||
|
{
|
||||||
|
// can be any supported AES algorithm ("AES-CTR", "AES-CBC", "AES-CMAC", "AES-GCM", "AES-CFB", "AES-KW", "ECDH", "DH" or "HMAC")
|
||||||
|
name: algo,
|
||||||
|
iv: StrToArr(atob(object.iv)), // the initialization vector you used to encrypt
|
||||||
|
additionalData: StrToArr(atob(object.adata)), // the addtional data you used during encryption (if any)
|
||||||
|
tagLength: object.ts, // the length of the tag you used to encrypt (if any)
|
||||||
|
},
|
||||||
|
derivedKey,
|
||||||
|
StrToArr(compress(message)) // compressed plain text to encrypt
|
||||||
|
)
|
||||||
|
return btoa(ArrToStr(encrypted));
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -646,18 +777,57 @@ jQuery.PrivateBin = (function($, sjcl, RawDeflate) {
|
||||||
* @param {string} data - JSON with encrypted data
|
* @param {string} data - JSON with encrypted data
|
||||||
* @return {string} decrypted message, empty if decryption failed
|
* @return {string} decrypted message, empty if decryption failed
|
||||||
*/
|
*/
|
||||||
me.decipher = function(key, password, data)
|
me.decipher = async function(key, password, data)
|
||||||
{
|
{
|
||||||
if (data !== undefined) {
|
try {
|
||||||
try {
|
if (password.length > 0) {
|
||||||
return decompress(sjcl.decrypt(key, data));
|
key += sjcl.codec.hex.fromBits(sjcl.hash.sha256.hash(password));
|
||||||
} catch(err) {
|
|
||||||
try {
|
|
||||||
return decompress(sjcl.decrypt(key + sjcl.codec.hex.fromBits(sjcl.hash.sha256.hash(password)), data));
|
|
||||||
} catch(e) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
var object = JSON.parse(data),
|
||||||
|
algo = 'AES-' + object.mode.toUpperCase();
|
||||||
|
|
||||||
|
// import raw key
|
||||||
|
var importedKey = await window.crypto.subtle.importKey(
|
||||||
|
'raw', // only 'raw' is allowed
|
||||||
|
StrToArr(key),
|
||||||
|
{name: 'PBKDF2'}, // we use PBKDF2 for key derivation
|
||||||
|
false, // the key may not be exported
|
||||||
|
["deriveKey"] // we may only use it for key derivation
|
||||||
|
)
|
||||||
|
|
||||||
|
// derive a stronger key for use with AES
|
||||||
|
var derivedKey = await window.crypto.subtle.deriveKey(
|
||||||
|
{
|
||||||
|
name: 'PBKDF2', // we use PBKDF2 for key derivation
|
||||||
|
salt: StrToArr(atob(object.salt)), // salt used in HMAC
|
||||||
|
iterations: object.iter, // amount of iterations to apply
|
||||||
|
hash: {name: "SHA-256"}, // can be "SHA-1", "SHA-256", "SHA-384" or "SHA-512"
|
||||||
|
},
|
||||||
|
importedKey,
|
||||||
|
{
|
||||||
|
// can be any supported AES algorithm ("AES-CTR", "AES-CBC", "AES-CMAC", "AES-GCM", "AES-CFB", "AES-KW", "ECDH", "DH" or "HMAC")
|
||||||
|
name: algo,
|
||||||
|
length: object.ks, // can be 128, 192 or 256
|
||||||
|
},
|
||||||
|
false, // the key may not be exported
|
||||||
|
["decrypt"] // we may only use it for decryption
|
||||||
|
)
|
||||||
|
|
||||||
|
// finally, decrypt message
|
||||||
|
var decrypted = await window.crypto.subtle.decrypt(
|
||||||
|
{
|
||||||
|
// can be any supported AES algorithm ("AES-CTR", "AES-CBC", "AES-CMAC", "AES-GCM", "AES-CFB", "AES-KW", "ECDH", "DH" or "HMAC")
|
||||||
|
name: algo,
|
||||||
|
iv: StrToArr(atob(object.iv)), // the initialization vector you used to encrypt
|
||||||
|
additionalData: StrToArr(atob(object.adata)), // the addtional data you used during encryption (if any)
|
||||||
|
tagLength: object.ts, // the length of the tag you used to encrypt (if any)
|
||||||
|
},
|
||||||
|
derivedKey,
|
||||||
|
StrToArr(atob(object.ct)) // cipher text to decrypt
|
||||||
|
)
|
||||||
|
return decompress(ArrToStr(decrypted));
|
||||||
|
} catch(err) {
|
||||||
|
return '';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -673,33 +843,7 @@ jQuery.PrivateBin = (function($, sjcl, RawDeflate) {
|
||||||
*/
|
*/
|
||||||
me.getSymmetricKey = function()
|
me.getSymmetricKey = function()
|
||||||
{
|
{
|
||||||
var crypto, key;
|
return btoa(getRandomBytes(32));
|
||||||
if (typeof module !== 'undefined' && module.exports) {
|
|
||||||
// node environment
|
|
||||||
key = require('crypto').randomBytes(32).toString('base64');
|
|
||||||
} else if (
|
|
||||||
typeof window !== 'undefined' &&
|
|
||||||
typeof Uint8Array !== 'undefined' &&
|
|
||||||
String.fromCodePoint &&
|
|
||||||
(
|
|
||||||
typeof window.crypto !== 'undefined' ||
|
|
||||||
typeof window.msCrypto !== 'undefined'
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
// modern browser environment
|
|
||||||
var bytes = '',
|
|
||||||
byteArray = new Uint8Array(32),
|
|
||||||
crypto = window.crypto || window.msCrypto;
|
|
||||||
crypto.getRandomValues(byteArray);
|
|
||||||
for (var i = 0; i < 32; ++i) {
|
|
||||||
bytes += String.fromCharCode(byteArray[i]);
|
|
||||||
}
|
|
||||||
key = btoa(bytes);
|
|
||||||
} else {
|
|
||||||
// legacy browser or unsupported environment
|
|
||||||
throw 'No supported crypto API detected, you may read pastes and post comments, but can\'t create pastes.';
|
|
||||||
}
|
|
||||||
return key;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return me;
|
return me;
|
||||||
|
|
|
@ -10,10 +10,12 @@ describe('CryptTool', function () {
|
||||||
'string',
|
'string',
|
||||||
'string',
|
'string',
|
||||||
function (key, password, message) {
|
function (key, password, message) {
|
||||||
|
jsdom();
|
||||||
|
window.crypto = new WebCrypto();
|
||||||
return message === $.PrivateBin.CryptTool.decipher(
|
return message === $.PrivateBin.CryptTool.decipher(
|
||||||
key,
|
key,
|
||||||
password,
|
password,
|
||||||
$.PrivateBin.CryptTool.cipher(key, password, message)
|
$.PrivateBin.CryptTool.cipher(key, password, message.trim())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
@ -25,15 +27,16 @@ describe('CryptTool', function () {
|
||||||
// SJCL based pastes still works
|
// SJCL based pastes still works
|
||||||
it(
|
it(
|
||||||
'supports PrivateBin v1 ciphertext (SJCL & browser atob)',
|
'supports PrivateBin v1 ciphertext (SJCL & browser atob)',
|
||||||
function () {
|
async function () {
|
||||||
delete global.Base64;
|
delete global.Base64;
|
||||||
// make btoa available
|
// make btoa available
|
||||||
jsdom();
|
jsdom();
|
||||||
global.btoa = window.btoa;
|
global.btoa = window.btoa;
|
||||||
|
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.
|
||||||
// Bonus points for finding their sources and hidden meanings.
|
// Bonus points for finding their sources and hidden meanings.
|
||||||
var paste1 = $.PrivateBin.CryptTool.decipher(
|
var paste1 = await $.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(function(_,b) { return b + 1; }).join(''),
|
||||||
|
@ -65,7 +68,7 @@ describe('CryptTool', function () {
|
||||||
'C5fARPJ4F2BIWgzgzkNj+dVjusft2XnziamWdbS5u3kuRlVuz5LQj+R5' +
|
'C5fARPJ4F2BIWgzgzkNj+dVjusft2XnziamWdbS5u3kuRlVuz5LQj+R5' +
|
||||||
'imnqQAincdZTkTT1nYx+DatlOLllCYIHffpI="}'
|
'imnqQAincdZTkTT1nYx+DatlOLllCYIHffpI="}'
|
||||||
),
|
),
|
||||||
paste2 = $.PrivateBin.CryptTool.decipher(
|
paste2 = await $.PrivateBin.CryptTool.decipher(
|
||||||
's9pmKZKOBN7EVvHpTA8jjLFH3Xlz/0l8lB4+ONPACrM=',
|
's9pmKZKOBN7EVvHpTA8jjLFH3Xlz/0l8lB4+ONPACrM=',
|
||||||
'', // no password
|
'', // no password
|
||||||
'{"iv":"WA42mdxIVXUwBqZu7JYNiw==","v":1,"iter":10000,"ks"' +
|
'{"iv":"WA42mdxIVXUwBqZu7JYNiw==","v":1,"iter":10000,"ks"' +
|
||||||
|
@ -101,12 +104,14 @@ describe('CryptTool', function () {
|
||||||
|
|
||||||
it(
|
it(
|
||||||
'supports ZeroBin ciphertext (SJCL & Base64 1.7)',
|
'supports ZeroBin ciphertext (SJCL & Base64 1.7)',
|
||||||
function () {
|
async function () {
|
||||||
global.Base64 = require('../base64-1.7').Base64;
|
global.Base64 = require('../base64-1.7').Base64;
|
||||||
|
jsdom();
|
||||||
|
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.
|
||||||
// Bonus points for finding their sources and hidden meanings.
|
// Bonus points for finding their sources and hidden meanings.
|
||||||
var paste1 = $.PrivateBin.CryptTool.decipher(
|
var paste1 = await $.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(function(_,b) { return b + 1; }).join(''),
|
||||||
|
@ -130,7 +135,7 @@ describe('CryptTool', function () {
|
||||||
'V37AeiNoD2PcI6ZcHbRdPa+XRrRcJhSPPW7UQ0z4OvBfjdu/w390QxAx' +
|
'V37AeiNoD2PcI6ZcHbRdPa+XRrRcJhSPPW7UQ0z4OvBfjdu/w390QxAx' +
|
||||||
'SxvZewoh49fKKB6hTsRnZb4tpHkjlww=="}'
|
'SxvZewoh49fKKB6hTsRnZb4tpHkjlww=="}'
|
||||||
),
|
),
|
||||||
paste2 = $.PrivateBin.CryptTool.decipher(
|
paste2 = await $.PrivateBin.CryptTool.decipher(
|
||||||
's9pmKZKOBN7EVvHpTA8jjLFH3Xlz/0l8lB4+ONPACrM=',
|
's9pmKZKOBN7EVvHpTA8jjLFH3Xlz/0l8lB4+ONPACrM=',
|
||||||
'', // no password
|
'', // no password
|
||||||
'{"iv":"Z7lAZQbkrqGMvruxoSm6Pw==","v":1,"iter":10000,"ks"' +
|
'{"iv":"Z7lAZQbkrqGMvruxoSm6Pw==","v":1,"iter":10000,"ks"' +
|
||||||
|
@ -167,6 +172,8 @@ describe('CryptTool', function () {
|
||||||
'returns random, non-empty keys',
|
'returns random, non-empty keys',
|
||||||
'integer',
|
'integer',
|
||||||
function(counter) {
|
function(counter) {
|
||||||
|
jsdom();
|
||||||
|
window.crypto = new WebCrypto();
|
||||||
var key = $.PrivateBin.CryptTool.getSymmetricKey(),
|
var key = $.PrivateBin.CryptTool.getSymmetricKey(),
|
||||||
result = (key !== '' && keys.indexOf(key) === -1);
|
result = (key !== '' && keys.indexOf(key) === -1);
|
||||||
keys.push(key);
|
keys.push(key);
|
||||||
|
|
|
@ -213,15 +213,14 @@ describe('Helper', function () {
|
||||||
this.timeout(30000);
|
this.timeout(30000);
|
||||||
jsc.property(
|
jsc.property(
|
||||||
'returns the requested cookie',
|
'returns the requested cookie',
|
||||||
'nearray asciinestring',
|
jsc.nearray(jsc.nearray(common.jscAlnumString())),
|
||||||
'nearray asciistring',
|
jsc.nearray(jsc.nearray(common.jscAlnumString())),
|
||||||
function (labels, values) {
|
function (labels, values) {
|
||||||
var selectedKey = '', selectedValue = '',
|
var selectedKey = '', selectedValue = '',
|
||||||
cookieArray = [];
|
cookieArray = [];
|
||||||
labels.forEach(function(item, i) {
|
labels.forEach(function(item, i) {
|
||||||
// deliberatly using a non-ascii key for replacing invalid characters
|
var key = item.join(''),
|
||||||
var key = item.replace(/[\s;,=]/g, Array(i+2).join('£')),
|
value = (values[i] || values[0]).join('');
|
||||||
value = (values[i] || values[0]).replace(/[\s;,=]/g, '');
|
|
||||||
cookieArray.push(key + '=' + value);
|
cookieArray.push(key + '=' + value);
|
||||||
if (Math.random() < 1 / i || selectedKey === key)
|
if (Math.random() < 1 / i || selectedKey === key)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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-MBMWkPzbqMbXFxOAWrJ50mzsZLDbDHKL+1z8uJ4E6nJ7BpAUYh7KnxV9GxuSKS7ZTguVoatCdG1KJ1MjdSimmQ==" crossorigin="anonymous"></script>
|
<script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-O3MbgA0NDp6Lklnj0GfSs7l5UkuuF/C+/rqjhVwr/Qw/b3jNEyBsl0ufQ3ruweBH10M189aquTnrmSifu1hDlA==" 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-MBMWkPzbqMbXFxOAWrJ50mzsZLDbDHKL+1z8uJ4E6nJ7BpAUYh7KnxV9GxuSKS7ZTguVoatCdG1KJ1MjdSimmQ==" crossorigin="anonymous"></script>
|
<script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-O3MbgA0NDp6Lklnj0GfSs7l5UkuuF/C+/rqjhVwr/Qw/b3jNEyBsl0ufQ3ruweBH10M189aquTnrmSifu1hDlA==" 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]-->
|
||||||
|
|
|
@ -69,6 +69,12 @@ $ npm install jsverify jsdom@9 jsdom-global@2 mime-types
|
||||||
Note: If you use a distribution that provides nodeJS >= 6, then you can install
|
Note: If you use a distribution that provides nodeJS >= 6, then you can install
|
||||||
the latest jsdom and jsdom-global packages and don't need to use @9 and @2.
|
the latest jsdom and jsdom-global packages and don't need to use @9 and @2.
|
||||||
|
|
||||||
|
Note: When running Ubuntu 18.04, there is [a bug](https://bugs.launchpad.net/ubuntu/+source/nodejs/+bug/1779863)
|
||||||
|
due to the mismatch of nodejs 8 and OpenSSL 1.1 library it was compiled against.
|
||||||
|
Until this is solved, you may have to use [a PPA of nodejs, compiled against
|
||||||
|
OpenSSL 1.0](https://launchpad.net/~ddstreet/+archive/ubuntu/lp1779863) or use
|
||||||
|
nodejs 10 or later from a different source.
|
||||||
|
|
||||||
To run the tests, just change into the `js` directory and run istanbul:
|
To run the tests, just change into the `js` directory and run istanbul:
|
||||||
```console
|
```console
|
||||||
$ cd PrivateBin/js
|
$ cd PrivateBin/js
|
||||||
|
|
Loading…
Reference in New Issue