implement sodium-plus library to replace unmaintained sjcl

This commit is contained in:
Mario
2024-03-10 22:35:59 +00:00
parent 0a730935f5
commit ee8aba3221
6 changed files with 104 additions and 29 deletions

View File

@@ -247,41 +247,39 @@ function bb_parse_crypt($match) {
$matches = [];
$attributes = $match[1];
$algorithm = "";
$hint = '';
$algorithm = '';
preg_match("/alg='(.*?)'/ism", $attributes, $matches);
if ($matches[1] != "")
$algorithm = $matches[1];
$algorithm = $matches[1] ?? '';
preg_match("/alg=\&quot\;(.*?)\&quot\;/ism", $attributes, $matches);
if ($matches[1] != "")
$algorithm = $matches[1];
$hint = "";
if (!$algorithm) {
preg_match("/alg=\&quot\;(.*?)\&quot\;/ism", $attributes, $matches);
$algorithm = $matches[1] ?? '';
}
preg_match("/hint='(.*?)'/ism", $attributes, $matches);
if ($matches[1] != "")
$hint = $matches[1];
preg_match("/hint=\&quot\;(.*?)\&quot\;/ism", $attributes, $matches);
if ($matches[1] != "")
$hint = $matches[1];
$hint = $matches[1] ?? '';
if (!$hint) {
preg_match("/hint=\&quot\;(.*?)\&quot\;/ism", $attributes, $matches);
$hint = $matches[1] ?? '';
}
$x = random_string();
$f = 'hz_decrypt';
$f = 'sodium_decrypt';
//legacy cryptojs support
if(plugin_is_installed('cryptojs')) {
$f = ((in_array($algorithm, ['AES-128-CCM', 'rot13', 'triple-rot13'])) ? 'hz_decrypt' : 'red_decrypt');
if (in_array($algorithm, ['AES-128-CCM', 'rot13', 'triple-rot13'])) {
$f = 'hz_decrypt'; // deprecated
}
$onclick = 'onclick="' . $f . '(\'' . $algorithm . '\',\'' . $hint . '\',\'' . $match[2] . '\',\'#' . $x . '\');"';
$label = t('Encrypted content');
$Text = '<br /><div id="' . $x . '"><img class="cursor-pointer" src="' . z_root() . '/images/lock_icon.svg" ' . $onclick . ' alt="' . $label . '" title="' . $label . '" /></div><br />';
$text = '<br /><div id="' . $x . '"><img class="cursor-pointer" src="' . z_root() . '/images/lock_icon.svg" ' . $onclick . ' alt="' . $label . '" title="' . $label . '" /></div><br />';
return $Text;
return $text;
}
/**

View File

@@ -15,6 +15,82 @@ function str_rot13 (str) {
});
}
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();
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 s = await sodium.sodium_bin2hex(salt);
let n = await sodium.sodium_bin2hex(nonce);
let c = await sodium.sodium_bin2hex(ciphertext);
let encrypted = window.btoa(s + '.' + n + '.' + c);
let val = "[crypt alg='XSalsa20' hint='" + hint + "']" + encrypted + '[/crypt]';
$(element).val(val);
}
async function sodium_decrypt(alg, hint, encrypted, element) {
if (alg !== 'XSalsa20') {
alert('Unsupported algorithm');
return false;
}
let arr = window.atob(encrypted).split('.');
let salt = await sodium.sodium_hex2bin(arr[0]);
let nonce = await sodium.sodium_hex2bin(arr[1]);
let ciphertext = await sodium.sodium_hex2bin(arr[2]);
let password = prompt((hint.length) ? hex2bin(hint) : aStr['passphrase']);
if (!password) {
return false;
}
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(newdiv);
}
else {
$(element).html(decrypted.toString('utf-8'));
}
}
function hz_encrypt(alg, elem) {
var enc_text = '';
var newdiv = '';
@@ -33,7 +109,7 @@ function hz_encrypt(alg, elem) {
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.
// but consequently isn't secure.
if(! enc_key)
alg = 'rot13';
@@ -44,7 +120,7 @@ function hz_encrypt(alg, elem) {
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.
// Maybe "Grandma's maiden name" or "our secret place" or something.
var enc_hint = bin2hex(prompt(aStr['passhint']));
@@ -62,7 +138,7 @@ function hz_encrypt(alg, elem) {
// 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);
}
@@ -98,8 +174,8 @@ function hz_decrypt(alg, hint, text, elem) {
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
// 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.

View File

@@ -26,6 +26,7 @@ head_add_js('/library/readmore.js/readmore.js');
head_add_js('/library/jgrowl/jquery.jgrowl.min.js');
head_add_js('/library/sjcl/sjcl.js');
head_add_js('/library/sodium-plus/dist/sodium-plus.min.js');
head_add_js('acl.js');
head_add_js('webtoolkit.base64.js');

View File

@@ -70,7 +70,7 @@
</div>
{{if $feature_encrypt}}
<div class="btn-group me-2 d-none d-md-flex">
<button id="chat-encrypt-wrapper" class="btn btn-outline-secondary btn-sm" onclick="hz_encrypt('{{$cipher}}', '#chatText'); return false;">
<button id="chat-encrypt-wrapper" class="btn btn-outline-secondary btn-sm" onclick="sodium_encrypt('#chatText'); return false;">
<i id="chat-encrypt" class="fa fa-key jot-icons" title="{{$encrypt}}" ></i>
</button>
</div>
@@ -83,7 +83,7 @@
<a class="dropdown-item" href="#" onclick="chatJotGetLink(); return false;" ><i class="fa fa-link"></i>&nbsp;{{$insert}}</a>
{{if $feature_encrypt}}
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="#" onclick="hz_encrypt('{{$cipher}}', '#chatText'); return false;"><i class="fa fa-key"></i>&nbsp;{{$encrypt}}</a>
<a class="dropdown-item" href="#" onclick="sodium_encrypt('#chatText'); return false;"><i class="fa fa-key"></i>&nbsp;{{$encrypt}}</a>
{{/if}}
</div>
</div>

View File

@@ -50,7 +50,7 @@
</div>
{{if $feature_encrypt}}
<div class="btn-group me-2">
<button class="btn btn-outline-secondary btn-sm border-0" title="{{$encrypt}}" onclick="hz_encrypt('{{$cipher}}','#comment-edit-text-' + '{{$id}}'); return false;">
<button class="btn btn-outline-secondary btn-sm border-0" title="{{$encrypt}}" onclick="sodium_encrypt('#comment-edit-text-' + '{{$id}}'); return false;">
<i class="fa fa-key comment-icon"></i>
</button>
</div>

View File

@@ -160,7 +160,7 @@
</button>
{{/if}}
{{if $feature_encrypt}}
<button type="button" id="profile-encrypt-wrapper" class="btn btn-outline-secondary btn-sm border-0" title="{{$encrypt}}" onclick="hz_encrypt('{{$cipher}}','#profile-jot-text');return false;">
<button type="button" id="profile-encrypt-wrapper" class="btn btn-outline-secondary btn-sm border-0" title="{{$encrypt}}" onclick="sodium_encrypt('#profile-jot-text'); return false;">
<i id="profile-encrypt" class="fa fa-key jot-icons"></i>
</button>
{{/if}}