From dddcddc453bdbd59e1cafcb8ca8aeb2225dfda9d Mon Sep 17 00:00:00 2001 From: Mario Date: Fri, 15 Mar 2024 11:30:28 +0000 Subject: refactor sodium b2b encryption --- include/bbcode.php | 40 +++++++++++++++++++++++----------------- view/js/crypto.js | 38 +++++++++++++++++++++++++------------- 2 files changed, 48 insertions(+), 30 deletions(-) diff --git a/include/bbcode.php b/include/bbcode.php index b39822b05..20a866073 100644 --- a/include/bbcode.php +++ b/include/bbcode.php @@ -246,38 +246,45 @@ function bb_replace_images($body, $images) { function bb_parse_crypt($match) { $matches = []; - $attributes = $match[1]; $hint = ''; $algorithm = ''; + $payload = $match[1]; + + if (isset($match[2])) { + // backwards compatibility - preg_match("/alg='(.*?)'/ism", $attributes, $matches); - $algorithm = $matches[1] ?? ''; + $attributes = $match[1]; + $payload = $match[2]; - if (!$algorithm) { - preg_match("/alg=\"\;(.*?)\"\;/ism", $attributes, $matches); + preg_match("/alg='(.*?)'/ism", $attributes, $matches); $algorithm = $matches[1] ?? ''; - } - preg_match("/hint='(.*?)'/ism", $attributes, $matches); - $hint = $matches[1] ?? ''; + if (!$algorithm) { + preg_match("/alg=\"\;(.*?)\"\;/ism", $attributes, $matches); + $algorithm = $matches[1] ?? ''; + } - if (!$hint) { - preg_match("/hint=\"\;(.*?)\"\;/ism", $attributes, $matches); + preg_match("/hint='(.*?)'/ism", $attributes, $matches); $hint = $matches[1] ?? ''; + + if (!$hint) { + preg_match("/hint=\"\;(.*?)\"\;/ism", $attributes, $matches); + $hint = $matches[1] ?? ''; + } } - $x = random_string(); + $x = random_string(32); - $f = 'sodium_decrypt'; + $onclick = 'onclick="sodium_decrypt(\'' . $payload . '\',\'#' . $x . '\');"'; if (in_array($algorithm, ['AES-128-CCM', 'rot13', 'triple-rot13'])) { - $f = 'hz_decrypt'; // deprecated + // backwards compatibility + $onclick = 'onclick="hz_decrypt(\'' . $algorithm . '\',\'' . $hint . '\',\'' . $payload . '\',\'#' . $x . '\');"'; } - $onclick = 'onclick="' . $f . '(\'' . $algorithm . '\',\'' . $hint . '\',\'' . $match[2] . '\',\'#' . $x . '\');"'; $label = t('Encrypted content'); - $text = '
' . $label . '

'; + $text = '
' . $label . '
'; return $text; } @@ -1627,8 +1634,7 @@ function bbcode($text, $options = []) { // crypt if (strpos($text,'[/crypt]') !== false) { - $x = random_string(); - $text = preg_replace("/\[crypt\](.*?)\[\/crypt\]/ism",'
' . t('Encrypted content') . '
', $text); + $text = preg_replace_callback("/\[crypt\](.*?)\[\/crypt\]/ism", 'bb_parse_crypt', $text); $text = preg_replace_callback("/\[crypt (.*?)\](.*?)\[\/crypt\]/ism", 'bb_parse_crypt', $text); } diff --git a/view/js/crypto.js b/view/js/crypto.js index 55b9aea11..bc399caa3 100644 --- a/view/js/crypto.js +++ b/view/js/crypto.js @@ -25,6 +25,11 @@ async function sodium_encrypt(element) { } let message = $(element).val(); + + if (!message) { + return false; + } + let password = prompt(aStr['passphrase']); if (!password) { @@ -35,6 +40,7 @@ async function sodium_encrypt(element) { let salt = await sodium.randombytes_buf(16); let nonce = await sodium.randombytes_buf(24); + let key = await sodium.crypto_pwhash( 32, password, @@ -47,31 +53,37 @@ async function sodium_encrypt(element) { 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]'; + 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(alg, hint, encrypted, element) { - if (alg !== 'XSalsa20') { +async function sodium_decrypt(payload, element) { + let arr = JSON.parse(window.atob(payload)); + + if (arr.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']); + 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, @@ -84,7 +96,7 @@ async function sodium_decrypt(alg, hint, encrypted, element) { delete password, key; if ($(element).css('display') === 'none' && typeof tinyMCE !== typeof undefined) { - tinyMCE.activeEditor.setContent(newdiv); + tinyMCE.activeEditor.setContent(decrypted.toString('utf-8')); } else { $(element).html(decrypted.toString('utf-8')); -- cgit v1.2.3