getEncryptionKey(); if (function_exists('sodium_crypto_secretbox') && extension_loaded('sodium')) { $nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES); $ciphertext = sodium_crypto_secretbox($value, $nonce, $key); // Prepend nonce to ciphertext and encode. return base64_encode('sodium:' . $nonce . $ciphertext); } if (extension_loaded('openssl')) { $ivLength = 12; // Standard IV length for AES-256-GCM. $iv = random_bytes($ivLength); $tag = ''; $ciphertext = openssl_encrypt( $value, 'aes-256-gcm', $key, OPENSSL_RAW_DATA, $iv, $tag ); if ($ciphertext === false) { return ''; } // Prepend iv and tag to ciphertext and encode. return base64_encode('openssl:' . $iv . $tag . $ciphertext); } return ''; } /** * @inheritDoc */ public function decrypt(string $value): string { if ($value === '') { return ''; } $decoded = base64_decode($value); if ($decoded === false) { return ''; } $key = $this->getEncryptionKey(); // Check if Sodium encrypted. if (strpos($decoded, 'sodium:') === 0) { if (!function_exists('sodium_crypto_secretbox') || !extension_loaded('sodium')) { // If sodium is no longer available, we cannot decrypt sodium-encrypted values. return ''; } $payload = substr($decoded, 7); // Strip 'sodium:' prefix. $nonce = substr($payload, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES); $ciphertext = substr($payload, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES); $decrypted = sodium_crypto_secretbox_open($ciphertext, $nonce, $key); return $decrypted !== false ? $decrypted : ''; } // Check if OpenSSL encrypted. if (strpos($decoded, 'openssl:') === 0) { if (!extension_loaded('openssl')) { return ''; } $payload = substr($decoded, 8); // Strip 'openssl:' prefix. $ivLength = 12; $tagLength = 16; // GCM default tag length. $iv = substr($payload, 0, $ivLength); $tag = substr($payload, $ivLength, $tagLength); $ciphertext = substr($payload, $ivLength + $tagLength); $decrypted = openssl_decrypt( $ciphertext, 'aes-256-gcm', $key, OPENSSL_RAW_DATA, $iv, $tag ); return $decrypted !== false ? $decrypted : ''; } // Return empty if prefix is unknown. return ''; } /** * Derive a 32-byte encryption key from WordPress salts and a site salt option. * * @return string 32-byte binary key. */ private function getEncryptionKey(): string { $salt = ''; if (defined('SECURE_AUTH_KEY')) { $salt .= SECURE_AUTH_KEY; } if (defined('LOGGED_IN_KEY')) { $salt .= LOGGED_IN_KEY; } $siteSalt = get_option(self::SITE_SALT_OPTION); if (!$siteSalt) { $siteSalt = wp_generate_password(64, true, true); update_option(self::SITE_SALT_OPTION, $siteSalt); } $salt .= $siteSalt; return hash('sha256', $salt, true); } }