LinkProtection/core/crypto.php

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,
);
}
}