111 lines
2.8 KiB
PHP
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
|
|
);
|
|
}
|
|
}
|