From be4715096f9a63d823da90c405f500d454247989 Mon Sep 17 00:00:00 2001 From: Patrick Canterino Date: Thu, 28 Mar 2024 17:50:24 +0100 Subject: [PATCH 1/1] Re-emplementing encryption using Sodium Changes: - Encryption using Sodium - Key is generated using random_bytes() (cryptographically secure) - Encryption is done using a second file, which is renamed after encryption is complete (by using the same file, we would encrypt already encrypted data again) - A file encrypted using Sodium is marked with "C2" in the link file, so we can distinguish them from files encrypted using mcrypt ToDo: - Error checking - Show a warning in the admin interface if Sodium is not available --- f.php | 24 ++++++++++++ lib/functions.php | 96 ++++++++++++++++++++++++++++++++++------------- lib/settings.php | 2 + 3 files changed, 95 insertions(+), 27 deletions(-) diff --git a/f.php b/f.php index 0368d97..854da83 100644 --- a/f.php +++ b/f.php @@ -250,6 +250,30 @@ if ($cfg['litespeed_workaround']) { } /* Read encrypted file. */ elseif ($link['crypted']) { + /* Decrypt file. */ + $r = fopen(VAR_FILES . $p . $link['hash'], 'rb'); + $fs = fstat($r)['size']; + + $crypt_header = fread($r, SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES); + + /* Init module. */ + $crypt_state = sodium_crypto_secretstream_xchacha20poly1305_init_pull($crypt_header, $crypt_key); + + /* Decrypt file. */ + + for ($i = SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES; $i < $fs; $i += JIRAFEAU_SODIUM_CHUNKSIZE + SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES) { + $to_dec = fread($r, JIRAFEAU_SODIUM_CHUNKSIZE + SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES); + [$dec, $crypt_tag] = sodium_crypto_secretstream_xchacha20poly1305_pull($crypt_state, $to_dec); + echo $dec; + } + + fclose($r); + + /* Cleanup. */ + sodium_memzero($crypt_state); +} +/* Read encrypted file (legacy mode using mcrypt). */ +elseif ($link['crypted_legacy']) { /* Init module */ $m = mcrypt_module_open('rijndael-256', '', 'ofb', ''); /* Extract key and iv. */ diff --git a/lib/functions.php b/lib/functions.php index ecd7be8..c42293b 100644 --- a/lib/functions.php +++ b/lib/functions.php @@ -454,13 +454,14 @@ function jirafeau_upload($file, $one_time_download, $key, $time, $ip, $crypt, $l /* Crypt file if option is enabled. */ $crypted = false; $crypt_key = ''; - if ($crypt == true && !(extension_loaded('mcrypt') == true)) { - error_log("PHP extension mcrypt not loaded, won't encrypt in Jirafeau"); + if ($crypt == true && !(extension_loaded('sodium') == true)) { + error_log("PHP extension sodium not loaded, won't encrypt in Jirafeau"); } - if ($crypt == true && extension_loaded('mcrypt') == true) { - $crypt_key = jirafeau_encrypt_file($file['tmp_name'], $file['tmp_name']); + if ($crypt == true && extension_loaded('sodium') == true) { + $crypt_key = jirafeau_encrypt_file($file['tmp_name'], $file['tmp_name'].'crypt'); if (strlen($crypt_key) > 0) { $crypted = true; + rename($file['tmp_name'].'crypt', $file['tmp_name']); } } @@ -515,7 +516,7 @@ function jirafeau_upload($file, $one_time_download, $key, $time, $ip, $crypt, $l $handle, $name . NL. $mime_type . NL. $size . NL. $password . NL. $time . NL . $hash. NL . ($one_time_download ? 'O' : 'R') . NL . time() . - NL . $ip . NL. $delete_link_code . NL . ($crypted ? 'C' : 'O') + NL . $ip . NL. $delete_link_code . NL . ($crypted ? 'C2' : 'O') ); fclose($handle); $hash_link = substr(base_16_to_64(md5_file($link_tmp_name)), 0, $link_name_length); @@ -664,7 +665,8 @@ function jirafeau_get_link($hash) $out['upload_date'] = trim($c[7]); $out['ip'] = trim($c[8]); $out['link_code'] = trim($c[9]); - $out['crypted'] = trim($c[10]) == 'C'; + $out['crypted'] = trim($c[10]) == 'C2'; + $out['crypted_legacy'] = trim($c[10]) == 'C'; return $out; } @@ -900,6 +902,7 @@ function jirafeau_admin_bug_report($cfg) $out .= "# PHP options
"; $out .= "- php version: " . phpversion() . "
"; + $out .= "- sodium version: " . phpversion('sodium') . "
"; $out .= "- mcrypt version: " . phpversion('mcrypt') . "
"; $php_options = [ 'post_max_size', @@ -1141,10 +1144,12 @@ function jirafeau_async_end($ref, $code, $crypt, $link_name_length, $file_hash_m $crypted = false; $crypt_key = ''; - if ($crypt == true && extension_loaded('mcrypt') == true) { - $crypt_key = jirafeau_encrypt_file($p, $p); + if ($crypt == true && extension_loaded('sodium') == true) { + //$crypt_key = jirafeau_encrypt_file($p, $p); + $crypt_key = jirafeau_encrypt_file($p, $p.'.crypt'); if (strlen($crypt_key) > 0) { $crypted = true; + rename($p.'.crypt', $p); } } @@ -1179,7 +1184,7 @@ function jirafeau_async_end($ref, $code, $crypt, $link_name_length, $file_hash_m $handle, $a['file_name'] . NL . $a['mime_type'] . NL . $size . NL . $a['key'] . NL . $a['time'] . NL . $hash . NL . $a['onetime'] . NL . - time() . NL . $a['ip'] . NL . $delete_link_code . NL . ($crypted ? 'C' : 'O') + time() . NL . $a['ip'] . NL . $delete_link_code . NL . ($crypted ? 'C2' : 'O') ); fclose($handle); $hash_link = substr(base_16_to_64(md5_file($link_tmp_name)), 0, $link_name_length); @@ -1215,35 +1220,34 @@ function jirafeau_crypt_create_iv($base, $size) function jirafeau_encrypt_file($fp_src, $fp_dst) { $fs = filesize($fp_src); - if ($fs === false || $fs == 0 || !(extension_loaded('mcrypt') == true)) { + if ($fs === false || $fs == 0 || !(extension_loaded('sodium') == true)) { return ''; } - /* Prepare module. */ - $m = mcrypt_module_open('rijndael-256', '', 'ofb', ''); /* Generate key. */ - $crypt_key = jirafeau_gen_random(10); - $hash_key = md5($crypt_key); - $iv = jirafeau_crypt_create_iv($hash_key, mcrypt_enc_get_iv_size($m)); + $crypt_key = bin2hex(random_bytes(SODIUM_CRYPTO_STREAM_XCHACHA20_KEYBYTES / 2)); /* Init module. */ - mcrypt_generic_init($m, $hash_key, $iv); + [$crypt_state, $crypt_header] = sodium_crypto_secretstream_xchacha20poly1305_init_push($crypt_key); /* Crypt file. */ - $r = fopen($fp_src, 'r'); - $w = fopen($fp_dst, 'c'); - while (!feof($r)) { - $to_enc = fread($r, 1024); - if (strlen($to_enc) > 0) { - $enc = mcrypt_generic($m, $to_enc); - if (fwrite($w, $enc) === false) { - return ''; - } + $r = fopen($fp_src, 'rb'); + $w = fopen($fp_dst, 'wb'); + fwrite($w, $crypt_header); + + for ($i = 0; $i < $fs; $i += JIRAFEAU_SODIUM_CHUNKSIZE) { + $to_enc = fread($r, JIRAFEAU_SODIUM_CHUNKSIZE); + $enc = sodium_crypto_secretstream_xchacha20poly1305_push($crypt_state, $to_enc); + + if (fwrite($w, $enc) === false) { + return ''; } } + fclose($r); fclose($w); + /* Cleanup. */ - mcrypt_generic_deinit($m); - mcrypt_module_close($m); + sodium_memzero($crypt_state); + return $crypt_key; } @@ -1255,6 +1259,44 @@ function jirafeau_encrypt_file($fp_src, $fp_dst) * @return key used to decrypt. a string of length 0 is returned if failed. */ function jirafeau_decrypt_file($fp_src, $fp_dst, $k) +{ + $fs = filesize($fp_src); + if ($fs === false || $fs == 0 || extension_loaded('sodium') == false) { + return false; + } + + /* Decrypt file. */ + $r = fopen(VAR_FILES . $p . $link['hash'], 'rb'); + + $crypt_header = fread($r, SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES); + + /* Init module. */ + $crypt_state = sodium_crypto_secretstream_xchacha20poly1305_init_pull($crypt_header, $crypt_key); + + /* Decrypt file. */ + + for ($i = SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES; $i < $fs; $i += JIRAFEAU_SODIUM_CHUNKSIZE + SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES) { + $to_dec = fread($r, JIRAFEAU_SODIUM_CHUNKSIZE + SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES); + [$dec, $crypt_tag] = sodium_crypto_secretstream_xchacha20poly1305_pull($crypt_state, $to_dec); + echo $dec; + } + + fclose($r); + + /* Cleanup. */ + sodium_memzero($crypt_state); + + return true; +} + +/** + * Decrypt file. + * @param $fp_src file path to the file to decrypt. + * @param $fp_dst file path to the file to write decrypted file (could be the same). + * @param $k string composed of the key and the iv separated by a point ('.') + * @return key used to decrypt. a string of length 0 is returned if failed. + */ +function jirafeau_decrypt_file_legacy($fp_src, $fp_dst, $k) { $fs = filesize($fp_src); if ($fs === false || $fs == 0 || extension_loaded('mcrypt') == false) { diff --git a/lib/settings.php b/lib/settings.php index e2173bd..7ecee3a 100644 --- a/lib/settings.php +++ b/lib/settings.php @@ -75,5 +75,7 @@ define('JIRAFEAU_MONTH', 2592000); // JIRAFEAU_DAY * 30 define('JIRAFEAU_QUARTER', 7776000); // JIRAFEAU_DAY * 90 define('JIRAFEAU_YEAR', 31536000); // JIRAFEAU_DAY * 365 +define('JIRAFEAU_SODIUM_CHUNKSIZE', 1024); + // set UTC as default timezone for all date/time functions date_default_timezone_set('UTC'); -- 2.34.1