key = sodium_crypto_generichash( $key_material . 'tagsmith', '', SODIUM_CRYPTO_SECRETBOX_KEYBYTES ); } /** * The default key material: AUTH_KEY . SECURE_AUTH_SALT, or, when either * constant is undefined (e.g. an old wp-config) or weak, WordPress's * wp_salt('auth') . wp_salt('secure_auth'), so the plugin never fatals and * never keys on a value anyone can read. Weak is what wp_salt() itself * treats as unset: the placeholder from wp-config-sample.php, or the same * value in both constants. A site with real, different salts gets the key * it always had. * * @param string|null $auth_key AUTH_KEY, or null when undefined. * @param string|null $secure_auth_salt SECURE_AUTH_SALT, or null when undefined. */ public static function default_key_material( #[\SensitiveParameter] ?string $auth_key, #[\SensitiveParameter] ?string $secure_auth_salt ): string { if ( null !== $auth_key && null !== $secure_auth_salt && $auth_key !== $secure_auth_salt && self::SAMPLE_PHRASE !== $auth_key && self::SAMPLE_PHRASE !== $secure_auth_salt ) { return $auth_key . $secure_auth_salt; } if ( function_exists( 'wp_salt' ) ) { return wp_salt( 'auth' ) . wp_salt( 'secure_auth' ); } return (string) $auth_key . (string) $secure_auth_salt; } /** * Encrypts a plaintext string. * * @param string $plain Plaintext to encrypt. * @return string The ciphertext, as 'v1:' followed by base64(nonce . cipher). */ public function encrypt( #[\SensitiveParameter] string $plain ): string { $nonce = random_bytes( SODIUM_CRYPTO_SECRETBOX_NONCEBYTES ); $cipher = sodium_crypto_secretbox( $plain, $nonce, $this->key ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode -- ciphertext framing, not obfuscation. return self::PREFIX . base64_encode( $nonce . $cipher ); } /** * Decrypts a ciphertext produced by encrypt(). * * @param string $cipher Ciphertext to decrypt. * @return string|null The plaintext, or null on any failure. */ public function decrypt( #[\SensitiveParameter] string $cipher ): ?string { if ( 0 !== strpos( $cipher, self::PREFIX ) ) { return null; } // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode -- ciphertext framing, not obfuscation. $decoded = base64_decode( substr( $cipher, strlen( self::PREFIX ) ), true ); if ( false === $decoded ) { return null; } $nonce_len = SODIUM_CRYPTO_SECRETBOX_NONCEBYTES; if ( strlen( $decoded ) <= $nonce_len + SODIUM_CRYPTO_SECRETBOX_MACBYTES ) { return null; } $nonce = substr( $decoded, 0, $nonce_len ); $ciphertext = substr( $decoded, $nonce_len ); $plain = sodium_crypto_secretbox_open( $ciphertext, $nonce, $this->key ); return false === $plain ? null : $plain; } }