robotstxt-manager/includes/class-robotstxt-manager-encryption.php
2026-08-12 13:18:40 +00:00

111 lines
2.8 KiB
PHP

<?php
/**
* AES-256-CBC encryption helper for the account-level API key.
*
* @package Robotstxt_Manager
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Class Robotstxt_Manager_Encryption
*
* Same symmetric AES-256-CBC pattern Plugins Core uses for its Forgejo
* token. Encrypted values are base64-encoded strings with the IV prepended
* to the ciphertext, safe for wp_options storage.
*/
class Robotstxt_Manager_Encryption {
/**
* OpenSSL cipher method.
*
* @var string
*/
private const CIPHER = 'aes-256-cbc';
/**
* Context string used to derive a plugin-specific key.
*
* @var string
*/
private const CONTEXT = 'robotstxt_manager_encryption_v1';
/**
* Encrypts a plaintext string and returns a base64-encoded payload.
*
* @param string $plaintext The value to encrypt.
*
* @return string Base64-encoded ciphertext, or empty string on failure.
*/
public static function encrypt( string $plaintext ): string {
if ( '' === $plaintext ) {
return '';
}
$key = self::derive_key();
$iv_length = openssl_cipher_iv_length( self::CIPHER );
if ( false === $iv_length ) {
return '';
}
$iv = openssl_random_pseudo_bytes( $iv_length );
$ciphertext = openssl_encrypt( $plaintext, self::CIPHER, $key, OPENSSL_RAW_DATA, $iv );
if ( false === $ciphertext ) {
return '';
}
return base64_encode( $iv . $ciphertext ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
}
/**
* Decrypts a base64-encoded payload previously produced by encrypt().
*
* @param string $encoded The base64-encoded ciphertext.
*
* @return string The original plaintext, or empty string on failure.
*/
public static function decrypt( string $encoded ): string {
if ( '' === $encoded ) {
return '';
}
$decoded = base64_decode( $encoded, true ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
if ( false === $decoded ) {
return '';
}
$iv_length = openssl_cipher_iv_length( self::CIPHER );
if ( false === $iv_length || strlen( $decoded ) <= $iv_length ) {
return '';
}
$key = self::derive_key();
$iv = substr( $decoded, 0, $iv_length );
$ciphertext = substr( $decoded, $iv_length );
$plaintext = openssl_decrypt( $ciphertext, self::CIPHER, $key, OPENSSL_RAW_DATA, $iv );
return false !== $plaintext ? $plaintext : '';
}
/**
* Derives a 32-byte encryption key from WordPress secret constants.
*
* @return string 32-byte raw key.
*/
private static function derive_key(): string {
$auth_key = defined( 'AUTH_KEY' ) ? AUTH_KEY : 'auth_key_not_defined';
$auth_salt = defined( 'AUTH_SALT' ) ? AUTH_SALT : 'auth_salt_not_defined';
return substr(
hash_hmac( 'sha256', self::CONTEXT, $auth_key . $auth_salt, true ),
0,
32
);
}
}