aboutsummaryrefslogblamecommitdiffstats
path: root/view/js/crypto.js
blob: bc399caa3ec20d7811fde1ba372df1716e9c624b (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
















                                                                                                 









                                                        




                             









                                                     
 











                                                                            








                                                                                



                            



                                                   



                                               
                                                                                          




                             



                                                                     











                                                                                          
                                                                             





                                                             

















                                                                             
                                         






                                                                                           
                                  

                                                                                                    
                                                                                            


                                                                         
                                                               


                                                        
                                                                                                                           








                                                                              
 








                                                                                
                                            
 
                          
 
                                                                 
 


                                                                                                                                                                      
         
 







                                                                                                  
                                  





                                                                          

                                                                                














                                                                             
 
function str_rot13 (str) {
  // http://kevin.vanzonneveld.net
  // +   original by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
  // +   improved by: Ates Goral (http://magnetiq.com)
  // +   bugfixed by: Onno Marsman
  // +   improved by: Rafa? Kukawski (http://blog.kukawski.pl)
  // *     example 1: str_rot13('Kevin van Zonneveld');
  // *     returns 1: 'Xriva ina Mbaariryq'
  // *     example 2: str_rot13('Xriva ina Mbaariryq');
  // *     returns 2: 'Kevin van Zonneveld'
  // *     example 3: str_rot13(33);
  // *     returns 3: '33'
	return (str + '').replace(/[a-z]/gi, function (s) {
		return String.fromCharCode(s.charCodeAt(0) + (s.toLowerCase() < 'n' ? 13 : -13));
	});
}

async function sodium_encrypt(element) {
	if (!window.sodium) {
		window.sodium = await SodiumPlus.auto();
	}

	if (typeof tinyMCE !== typeof undefined) {
		tinyMCE.triggerSave(false,true);
	}

	let message = $(element).val();

	if (!message) {
		return false;
	}

	let password = prompt(aStr['passphrase']);

	if (!password) {
		return false;
	}

	let hint = bin2hex(prompt(aStr['passhint']));

	let salt = await sodium.randombytes_buf(16);
	let nonce = await sodium.randombytes_buf(24);

	let key = await sodium.crypto_pwhash(
		32,
		password,
		salt,
		sodium.CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE,
		sodium.CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE
	);

	// Message can be a string, buffer, array, etc.
	let ciphertext = await sodium.crypto_secretbox(message, nonce, key);
	delete message, password, key;

	let payload = {
		hint: hint,
		alg: 'XSalsa20',
		salt: await sodium.sodium_bin2hex(salt),
		nonce: await sodium.sodium_bin2hex(nonce),
		ciphertext: await sodium.sodium_bin2hex(ciphertext)
	};

	let val = "[crypt]" + window.btoa(JSON.stringify(payload)) + '[/crypt]';

	$(element).val(val);
}

async function sodium_decrypt(payload, element) {
	let arr = JSON.parse(window.atob(payload));

	if (arr.alg !== 'XSalsa20') {
		alert('Unsupported algorithm');
		return false;
	}

	let password = prompt((arr.hint.length) ? hex2bin(arr.hint) : aStr['passphrase']);

	if (!password) {
		return false;
	}

	let salt = await sodium.sodium_hex2bin(arr.salt);
	let nonce = await sodium.sodium_hex2bin(arr.nonce);
	let ciphertext = await sodium.sodium_hex2bin(arr.ciphertext);

	let key = await sodium.crypto_pwhash(
		32,
		password,
		salt,
		sodium.CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE,
		sodium.CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE
	);

	let decrypted = await sodium.crypto_secretbox_open(ciphertext, nonce, key);
	delete password, key;

	if ($(element).css('display') === 'none' && typeof tinyMCE !== typeof undefined) {
		tinyMCE.activeEditor.setContent(decrypted.toString('utf-8'));
	}
	else {
		$(element).html(decrypted.toString('utf-8'));
	}
}

function hz_encrypt(alg, elem) {
	var enc_text = '';
	var newdiv = '';

	if(typeof tinyMCE !== "undefined")
		tinyMCE.triggerSave(false,true);

	var text = $(elem).val();

	// key and hint need to be localised

        var passphrase = prompt(aStr['passphrase']);
        // let the user cancel this dialogue
        if (passphrase == null)
                return false;
        var enc_key = bin2hex(passphrase);

	// If you don't provide a key you get rot13, which doesn't need a key
	// but consequently isn't secure.

	if(! enc_key)
		alg = 'rot13';

	if((alg == 'rot13') || (alg == 'triple-rot13'))
		newdiv = "[crypt alg='rot13']" + window.btoa(str_rot13(text)) + '[/crypt]';

	if(alg == 'AES-128-CCM') {

			// This is the prompt we're going to use when the receiver tries to open it.
			// Maybe "Grandma's maiden name" or "our secret place" or something.

			var enc_hint = bin2hex(prompt(aStr['passhint']));

			enc_text = sjcl.encrypt(enc_key, text);

			encrypted = enc_text.toString();

			newdiv = "[crypt alg='AES-128-CCM' hint='" + enc_hint + "']" + window.btoa(encrypted) + '[/crypt]';
	}

	enc_key = '';

	// This might be a comment box on a page with a tinymce editor
	// so check if there is a tinymce editor but also check the display
	// property of our source element - because a tinymce instance
	// will have display "none". If a normal textarea such as in a comment
	// box has display "none" you wouldn't be able to type in it.

	if($(elem).css('display') == 'none' && typeof tinyMCE !== "undefined") {
		tinyMCE.activeEditor.setContent(newdiv);
	}
	else {
		$(elem).val(newdiv);
	}

}

function hz_decrypt(alg, hint, text, elem) {

	var dec_text = '';

	var supported = ['AES-128-CCM', 'rot13', 'triple-rot13'];

	if(supported.indexOf(alg) < 0) {
		alert('Sorry, this encryption type is not supported anymore.\r\nConsider asking your admin to install the cryptojs addon for legacy crypto support.');
		return;
	}

	text = window.atob(text);

	if(alg == 'rot13' || alg == 'triple-rot13')
		dec_text = str_rot13(text);
	else {
		var enc_key = bin2hex(prompt((hint.length) ? hex2bin(hint) : aStr['passphrase']));
	}

	if(alg == 'AES-128-CCM') {
		dec_text = sjcl.decrypt(enc_key, text);
	}

	enc_key = '';

	// Not sure whether to drop this back in the conversation display.
	// It probably needs a lightbox or popup window because any conversation
	// updates could
	// wipe out the text and make you re-enter the key if it was in the
	// conversation. For now we do that so you can read it.

	var dec_result = dec_text.toString();
	delete dec_text;

	// incorrect decryptions *usually* but don't always have zero length
	// If the person typo'd let them try again without reloading the page
	// otherwise they'll have no "padlock" to click to try again.

	if(dec_result.length) {
		$(elem).html(b2h(dec_result));
		dec_result = '';
	}
}