/www/keys/2015_enc.key // Make sure that /www/keys/2015_enc.key is readable by the php-fpm user (e.g. www-data) // If you followed the Debian/Ubuntu guide for PHP 5.6 then run: apt-get install php5.6-mcrypt php5.6-mysqli // - If you compiled PHP 5 from source make sure it is configured with --with-mysqli=mysqlnd --with-mcrypt // Put this script anywhere and run it from the command line like: php5.6 write_mod_secret.php [secret] // If no secret is provided, a random one will be generated, put this into your TOTP client with key length 6 and period/interval 30 seconds // If you don't have a TOTP client, you can use something like https://totp.danhersam.com // This was designed for PHP 5, if you are porting to PHP 7.2.0+ then you will need to update the auth_encrypt/auth_decrypt functions in lib/auth.php // I only have PHP 5 setup so I can't test but in theory the following functions should work: /* function auth_encrypt($data) { $key = file_get_contents('/www/keys/2015_enc.key'); if (!$key) { return false; } // Use AES-256-CBC (256-bit key, 128-bit block size) $cipher = 'aes-256-cbc'; $iv_len = openssl_cipher_iv_length($cipher); $iv = openssl_random_pseudo_bytes($iv_len); $encrypted = openssl_encrypt($data, $cipher, $key, OPENSSL_RAW_DATA, $iv); if ($encrypted === false) { return false; } // Prepend IV to encrypted data (as in your original code) return $iv . $encrypted; } function auth_decrypt($data) { $key = file_get_contents('/www/keys/2015_enc.key'); if (!$key) { return false; } $cipher = 'aes-256-cbc'; $iv_len = openssl_cipher_iv_length($cipher); // Extract IV and ciphertext $iv = substr($data, 0, $iv_len); $ciphertext = substr($data, $iv_len); $decrypted = openssl_decrypt($ciphertext, $cipher, $key, OPENSSL_RAW_DATA, $iv); if ($decrypted === false) { return false; } // Remove null padding (PKCS7 or zero padding, as in mcrypt) return rtrim($decrypted, "\0"); } */ require_once '/www/global/yotsuba/lib/auth.php'; // Base32 encode function for TOTP secrets function base32_encode($data) { $alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'; $binary = ''; foreach (str_split($data) as $char) { $binary .= sprintf('%08b', ord($char)); } $base32 = ''; foreach (str_split($binary, 5) as $chunk) { if (strlen($chunk) < 5) { $chunk = str_pad($chunk, 5, '0', STR_PAD_RIGHT); } $base32 .= $alphabet[bindec($chunk)]; } return $base32; } require_once '/www/global/yotsuba/config/config_db.php'; if ($argc < 2 || $argc > 3) { echo "Usage: php write_mod_secret.php [secret]\n"; exit(1); } $username = $argv[1]; if ($argc === 3) { $secret = $argv[2]; } else { // Generate a random 10-byte (80-bit) secret, base32 encoded (16 chars) if (function_exists('random_bytes')) { // PHP7+ $random_bytes = random_bytes(10); } elseif (function_exists('openssl_random_pseudo_bytes')) { // PHP5 $random_bytes = openssl_random_pseudo_bytes(10); } $secret = base32_encode($random_bytes); echo "\nGenerated secret, put this in your TOTP client: $secret\n\n"; } $secretkey = auth_encrypt($secret); if ($secretkey === false) { echo "Encryption failed.\n"; exit(1); } $mysqli = new mysqli(SQLHOST_GLOBAL, SQLUSER_GLOBAL, SQLPASS_GLOBAL, SQLDB_GLOBAL); if ($mysqli->connect_errno) { echo "Failed to connect to MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error . "\n"; exit(1); } // Check auth_secret column type is compatible $colType = null; $colRes = $mysqli->query("SHOW COLUMNS FROM mod_users LIKE 'auth_secret'"); if ($colRes && $row = $colRes->fetch_assoc()) { $colType = strtoupper($row['Type']); // Acceptable: BINARY(64), VARBINARY(64), BLOB, MEDIUMBLOB, LONGBLOB, TINYBLOB if (!( $colType === 'BINARY(64)' || $colType === 'VARBINARY(64)' || strpos($colType, 'BLOB') !== false )) { echo "WARNING: The 'auth_secret' column type is '$colType'. It should be BINARY(64), VARBINARY(64), or a BLOB type.\n"; } } $stmt = $mysqli->prepare("UPDATE mod_users SET auth_secret = ? WHERE username = ?"); if (!$stmt) { echo "Prepare failed: (" . $mysqli->errno . ") " . $mysqli->error . "\n"; exit(1); } $stmt->bind_param('bs', $secretkey, $username); $stmt->send_long_data(0, $secretkey); if ($stmt->execute()) { echo "auth_secret updated successfully\n"; } else { echo "Update failed: (" . $stmt->errno . ") " . $stmt->error . "\n"; } $stmt->close(); $mysqli->close(); exit(); ?>