254 lines
9.0 KiB
PHP
254 lines
9.0 KiB
PHP
<?php
|
|
|
|
/**
|
|
* @author Kato Twofold
|
|
* @copyright MIT
|
|
*
|
|
* The class has full support for encryption of strings, provides validation for those
|
|
* and makes sure they can be decrypted on the other end, the key is extremely important
|
|
* and you MUST keep track of it and not lose it as there is no way of getting it back.
|
|
*
|
|
*/
|
|
class kpcrypt {
|
|
|
|
// The key to use in the encryption process
|
|
private $key = null;
|
|
|
|
/**
|
|
*
|
|
* Define the number of blocks that should be read from the source file for each chunk.
|
|
* For 'AES-128-CBC' each block consist of 16 bytes.
|
|
* So if we read 10,000 blocks we load 160kb into memory. You may adjust this value
|
|
* to read/write shorter or longer chunks.
|
|
*
|
|
* The higher this size is, the faster the file will be
|
|
* processed but the more memory will in turn be also used
|
|
*
|
|
*/
|
|
private $blocks = 10000;
|
|
|
|
/**
|
|
* A log of all of the errors, rather nice for debugging
|
|
* Please don't write to this, only read from it since
|
|
* it should only store errors about this class.
|
|
*/
|
|
private $errorLog = [];
|
|
|
|
// The encryption cipher to use
|
|
private $cipherMethod = "AES-256-CBC";
|
|
|
|
/**
|
|
* @param string $key [Optional] The key to use for encryption, if none is mentioned a random one will be generated
|
|
*/
|
|
public function __construct( string $key = null, string $cipherMethod = "AES-256-CBC" ) {
|
|
// If no key is mentioned, generate one
|
|
if ( empty($key) ) {
|
|
$key = bin2hex(openssl_random_pseudo_bytes("64"));
|
|
}
|
|
|
|
// Lowercase the cipher
|
|
$this->cipherMethod = strtolower($cipherMethod);
|
|
|
|
// Check if the encryption method is valid
|
|
if (!in_array($this->cipherMethod, openssl_get_cipher_methods())) {
|
|
// Cypher was not in the available ciphers list.
|
|
$this->errorLog[] = "[" . __LINE__ . "]" . $this->cipherMethod . " isn't a valid cipherMethod.";
|
|
return FALSE;
|
|
}
|
|
|
|
$this->key = $key;
|
|
}
|
|
|
|
/**
|
|
* Get the key that the instance is currently using, really useful for when you randomly generate it!
|
|
* @return string The key of the instance
|
|
*/
|
|
public function getKey() {
|
|
return $this->key;
|
|
}
|
|
|
|
/**
|
|
* Get the encryption blocks for files
|
|
*/
|
|
public function getEncryptionBlocks() {
|
|
return $this->blocks;
|
|
}
|
|
|
|
/**
|
|
* Redefine the encryption blocks size
|
|
*/
|
|
public function setEncryptionBlocks(int $size = 10000) {
|
|
// Make sure the size has a minimum of blocks read.
|
|
if ( $size < 1 ) $size = 1;
|
|
|
|
// Set the local blocks
|
|
$this->blocks = $size;
|
|
}
|
|
|
|
public function getErrors() {
|
|
return $this->errorLog;
|
|
}
|
|
|
|
// #region String Encryption
|
|
|
|
/**
|
|
* @param string $data The data to be encrypted, this can only encrypt strings.
|
|
* @param bool $integrity_check Check if the encrypted data can also be decypted, this will take 2x more time to process the data, but will make 100% sure that the data is safe and can be decrypted.
|
|
*/
|
|
function encryptData(string $data, bool $integrity_check = TRUE) {
|
|
// Grab the key from self-reference
|
|
$key = $this->key;
|
|
|
|
// To encrypt a string
|
|
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($this->cipherMethod));
|
|
|
|
// Encrypt the data
|
|
$encryptedData = openssl_encrypt($data, $this->cipherMethod, $key, OPENSSL_RAW_DATA, $iv);
|
|
|
|
// Append the iv to the encrypted data
|
|
$encryptedData = $iv . $encryptedData;
|
|
|
|
// base64 encrypt to make sure we don't lose bytes
|
|
$encryptedData = base64_encode($encryptedData);
|
|
|
|
// Check if we should verify the integrity of the encryption
|
|
if ($integrity_check === TRUE) {
|
|
// Test for decryption validity
|
|
if (md5($this->decryptData($encryptedData)) === md5($data)) {
|
|
// Put the output in the result
|
|
return $encryptedData;
|
|
} else {
|
|
// Assign the output to the result
|
|
$this->errorLog[] = "[" . __LINE__ . "]" . "Failed integrity check";
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
// Simply return the results
|
|
return $encryptedData;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param string $data The data to be encrypted, this can only encrypt strings.
|
|
*/
|
|
function decryptData(string $data) {
|
|
// Grab the key from self-reference
|
|
$key = $this->key;
|
|
|
|
// base64 decode the data.
|
|
$data = base64_decode($data);
|
|
|
|
// Get the length of the IV
|
|
$iv_length = openssl_cipher_iv_length($this->cipherMethod);
|
|
// Get the IV from the decoded data
|
|
$iv = substr($data, 0, $iv_length);
|
|
|
|
// Get the encrypted string from the data
|
|
$data = substr($data, $iv_length);
|
|
|
|
// Decrypt the data
|
|
$data = openssl_decrypt($data, $this->cipherMethod, $key, OPENSSL_RAW_DATA, $iv);
|
|
|
|
|
|
// Return the data
|
|
return $data;
|
|
}
|
|
|
|
// #endregion
|
|
|
|
|
|
/**
|
|
* File encryption should only be used on larger files ( 512kb+ )
|
|
* Since the string encryption will work better on the smaller files, since it's all
|
|
* in the memory, this simlpy pipes the file through itself, for every 1Mb file it only uses ~0.1Mb of memory with the default block size
|
|
*/
|
|
// #region File Encryption
|
|
|
|
public function encryptFile(string $fileInput, string $fileOutput, bool $integrity_check = TRUE) {
|
|
|
|
// To encrypt a string
|
|
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($this->cipherMethod));
|
|
|
|
// Try and open the destionation
|
|
if ( $fout = fopen($fileOutput, 'w') ) {
|
|
// Try and open the input file
|
|
if ($fin = fopen($fileInput, 'rb') ) {
|
|
// Place the IV at the beginning of the file for later decryption
|
|
fwrite($fout, $iv);
|
|
|
|
// Start reading from the input file
|
|
while ( !feof($fin) ) {
|
|
// Read in blocks of 16
|
|
$plaintext = fread($fin, 16 * $this->blocks);
|
|
$ciphertext = openssl_encrypt($plaintext, $this->cipherMethod, $this->key, OPENSSL_RAW_DATA, $iv);
|
|
// Use the first 16 bytes of the ciphertext as the next initialization vector
|
|
$iv = substr($ciphertext, 0, 16);
|
|
fwrite($fout, $ciphertext);
|
|
}
|
|
|
|
// Close the input file
|
|
fclose($fin);
|
|
|
|
} else {
|
|
$this->errorLog[] = "[" . __LINE__ . "]" . "Could not open input file for reading.";
|
|
// Close the output file
|
|
fclose($fout);
|
|
return false;
|
|
}
|
|
|
|
} else {
|
|
$this->errorLog[] = "[" . __LINE__ . "]" . "Could not open file output path for writing.";
|
|
return false;
|
|
}
|
|
|
|
|
|
// Close the output file
|
|
fclose($fout);
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* @param string $fileInput The path to the encrypted file we want to decrypt
|
|
* @param string $fileOutput The path to where to place the decrypted content
|
|
*/
|
|
public function decryptFile(string $fileInput, string $fileOutput) {
|
|
|
|
|
|
// Try and open the destionation
|
|
if ( $fout = fopen($fileOutput, 'w') ) {
|
|
// Try and open the input file
|
|
if ($fin = fopen($fileInput, 'rb') ) {
|
|
// Get the IV from the beginning of the file
|
|
$iv = fread($fin, openssl_cipher_iv_length($this->cipherMethod));
|
|
|
|
while (!feof($fin)) {
|
|
// we have to read one block more for decrypting than for encrypting
|
|
$ciphertext = fread($fin, 16 * ($this->blocks + 1));
|
|
$plaintext = openssl_decrypt($ciphertext, $this->cipherMethod, $this->key, OPENSSL_RAW_DATA, $iv);
|
|
// Use the first 16 bytes of the ciphertext as the next initialization vector
|
|
$iv = substr($ciphertext, 0, 16);
|
|
fwrite($fout, $plaintext);
|
|
}
|
|
|
|
// Close the input file
|
|
fclose($fin);
|
|
} else {
|
|
$this->errorLog[] = "[" . __LINE__ . "]" . "Could not open file input path for writing.";
|
|
return false;
|
|
}
|
|
// Close the output file
|
|
fclose($fout);
|
|
return 1;
|
|
} else {
|
|
$this->errorLog[] = "[" . __LINE__ . "]" . "Could not open file output path for writing.";
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
// #endregion
|
|
|
|
}
|
|
|
|
|