From ee8aba3221f995b265c3196505a1c7c26b76f116 Mon Sep 17 00:00:00 2001 From: Mario Date: Sun, 10 Mar 2024 22:35:59 +0000 Subject: implement sodium-plus library to replace unmaintained sjcl --- include/bbcode.php | 38 ++++++++++----------- view/js/crypto.js | 86 ++++++++++++++++++++++++++++++++++++++++++++--- view/php/theme_init.php | 1 + view/tpl/chat.tpl | 4 +-- view/tpl/comment_item.tpl | 2 +- view/tpl/jot.tpl | 2 +- 6 files changed, 104 insertions(+), 29 deletions(-) diff --git a/include/bbcode.php b/include/bbcode.php index 848d117fb..b39822b05 100644 --- a/include/bbcode.php +++ b/include/bbcode.php @@ -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]; - - preg_match("/alg=\"\;(.*?)\"\;/ism", $attributes, $matches); - if ($matches[1] != "") - $algorithm = $matches[1]; + $algorithm = $matches[1] ?? ''; - $hint = ""; + if (!$algorithm) { + preg_match("/alg=\"\;(.*?)\"\;/ism", $attributes, $matches); + $algorithm = $matches[1] ?? ''; + } preg_match("/hint='(.*?)'/ism", $attributes, $matches); - if ($matches[1] != "") - $hint = $matches[1]; - preg_match("/hint=\"\;(.*?)\"\;/ism", $attributes, $matches); - if ($matches[1] != "") - $hint = $matches[1]; + $hint = $matches[1] ?? ''; + + if (!$hint) { + preg_match("/hint=\"\;(.*?)\"\;/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 = '
' . $label . '

'; + $text = '
' . $label . '

'; - return $Text; + return $text; } /** diff --git a/view/js/crypto.js b/view/js/crypto.js index 14bc1e0a2..55b9aea11 100644 --- a/view/js/crypto.js +++ b/view/js/crypto.js @@ -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. diff --git a/view/php/theme_init.php b/view/php/theme_init.php index b08651689..50da8b5f9 100644 --- a/view/php/theme_init.php +++ b/view/php/theme_init.php @@ -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'); diff --git a/view/tpl/chat.tpl b/view/tpl/chat.tpl index da8da30f2..a0e528894 100644 --- a/view/tpl/chat.tpl +++ b/view/tpl/chat.tpl @@ -70,7 +70,7 @@ {{if $feature_encrypt}}
-
@@ -83,7 +83,7 @@  {{$insert}} {{if $feature_encrypt}} -  {{$encrypt}} +  {{$encrypt}} {{/if}} diff --git a/view/tpl/comment_item.tpl b/view/tpl/comment_item.tpl index d29193901..891d901c9 100644 --- a/view/tpl/comment_item.tpl +++ b/view/tpl/comment_item.tpl @@ -50,7 +50,7 @@ {{if $feature_encrypt}}
-
diff --git a/view/tpl/jot.tpl b/view/tpl/jot.tpl index 0ffdc0517..eedd92170 100644 --- a/view/tpl/jot.tpl +++ b/view/tpl/jot.tpl @@ -160,7 +160,7 @@ {{/if}} {{if $feature_encrypt}} - {{/if}} -- cgit v1.2.3