132 lines
3.3 KiB
PHP
Executable file
132 lines
3.3 KiB
PHP
Executable file
<?php
|
|
|
|
/*
|
|
* This is our crypto class which handles encryption and decryption of links.
|
|
* It is used by both the link replacement and protected link page.
|
|
* Encryption uses an explicit initialization vector.
|
|
* The user ID and post ID are encoded into a binary string and prepended to the plaintext so that they can be retrieved later upon decryption.
|
|
*/
|
|
|
|
namespace pedodev\linkprotection\core;
|
|
|
|
use phpbb\config\config;
|
|
|
|
class crypto
|
|
{
|
|
private const INT_32_SIZE = 4;
|
|
|
|
private string $key;
|
|
private string $cipher;
|
|
private int $iv_length;
|
|
private string $binary_vars;
|
|
|
|
public function __construct(config $config)
|
|
{
|
|
$this->key = base64_decode($config['pedodev_linkprotection_key']);
|
|
$this->cipher = $config['pedodev_linkprotection_cipher'];
|
|
$this->iv_length = openssl_cipher_iv_length($this->cipher);
|
|
var_dump($this->iv_length);
|
|
}
|
|
|
|
/*
|
|
* Packs two unsigned 32-bit integers into an 8-byte binary string
|
|
*/
|
|
public function set_vars(int $user_id, int $post_id): void
|
|
{
|
|
$this->binary_vars = pack('L2', $user_id, $post_id);
|
|
}
|
|
|
|
/*
|
|
* The inverse of the set_vars function.
|
|
* Unpacks the 8-byte binary string into an array of two integers
|
|
*/
|
|
private function get_binary_vars(string $plaintext): array|bool
|
|
{
|
|
$binary_string = substr($plaintext, $this->iv_length);
|
|
return unpack('Luser_id/Lpost_id', $binary_string);
|
|
}
|
|
|
|
/*
|
|
* Encodes base64 data so it can be used in URLs, replacing + / = with - _ . respectively
|
|
*/
|
|
private function base64_url_encode(string $input): string
|
|
{
|
|
return strtr($input, '+/=', '-_.');
|
|
}
|
|
|
|
/*
|
|
* The inverse of the base64_url_encode function
|
|
*/
|
|
private function base64_url_decode(string $input): string
|
|
{
|
|
return strtr($input, '-_.', '+/=');
|
|
}
|
|
|
|
/*
|
|
* Pad the plaintext with the explicit IV and the 8-byte binary string
|
|
*/
|
|
private function pad_link(string $link): string
|
|
{
|
|
$explicit_iv = openssl_random_pseudo_bytes($this->iv_length);
|
|
return $explicit_iv . $this->binary_vars . $link;
|
|
}
|
|
|
|
/*
|
|
* Get the decrypted link without the explicit IV or binary string
|
|
*/
|
|
private function get_link(string $plaintext): string
|
|
{
|
|
$link_offset = $this->iv_length + 2 * self::INT_32_SIZE;
|
|
return substr($plaintext, $link_offset);
|
|
}
|
|
|
|
/*
|
|
* Encrypt a given link and returns it in URL encoded base64
|
|
* Returns null on failure
|
|
*/
|
|
public function encrypt_link(string $link): ?string
|
|
{
|
|
$iv = openssl_random_pseudo_bytes($this->iv_length);
|
|
$padded_link = $this->pad_link($link);
|
|
|
|
$ciphertext = openssl_encrypt($padded_link, $this->cipher, $this->key, $options = 0, $iv);
|
|
return ($ciphertext ? $this->base64_url_encode($ciphertext) : null);
|
|
}
|
|
|
|
/*
|
|
* Decrypt data and returns an array containing the original link, the user ID and the post ID
|
|
* Returns null on failure
|
|
*/
|
|
public function decrypt_link(string $encoded_ciphertext): ?array
|
|
{
|
|
$ciphertext = $this->base64_url_decode($encoded_ciphertext);
|
|
$iv = openssl_random_pseudo_bytes($this->iv_length);
|
|
|
|
$plaintext = openssl_decrypt($ciphertext, $this->cipher, $this->key, $options = 0, $iv);
|
|
|
|
if (!$plaintext)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
$binary_vars = $this->get_binary_vars($plaintext);
|
|
|
|
if (!$binary_vars)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
$link = $this->get_link($plaintext);
|
|
|
|
if (!ctype_print($link))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return array(
|
|
'user_id' => $binary_vars['user_id'],
|
|
'post_id' => $binary_vars['post_id'],
|
|
'link' => $link,
|
|
);
|
|
}
|
|
}
|