Compare commits

..

13 Commits
main ... master

5 changed files with 439 additions and 76 deletions

4
.gitignore vendored
View File

@ -1 +1,5 @@
test_input_file.txt
config.inc.php config.inc.php
example.enc.php
example.dec.php
testdir

109
README.md
View File

@ -6,22 +6,111 @@ The Project is a simple php encryption wrapper, not even a library, the whole po
## Usage ## Usage
Simply use the provided functions from the lib.php file, you can simply import it into any project using that file. Simply use the provided functions from the lib.php file, you can simply import it into any project using that file.
#### String Encryption
```php ```php
require_once("/path/to/the/file/kpcrypt/lib.php") <?php
require_once("./lib.php");
@require_once("./config.inc.php");
$input = "Super secret string!"; // Check if the config succesfully loaded, or if the mandatory config fields are missing.
$key = "My key to encrypt the string with"; if ( empty($config) || empty($config['key']) || empty($config['input']) ) {
echo "Please copy the config.sample.inc.php to config.inc.php and change the configuration to match your needs.";
exit(1);
}
// Encrypt the data, the cipher can be easily changed, the integrity_check is recommended to be left to TRUE even if it takes some extra time to decrypt and check. // The memory limit has been set so that we can pass it big strings and test how much it can/will use.
$encryptedData = encryptData($input, $key, 'AES-256-CBC', TRUE); ini_set('memory_limit','1G');
// Echo out the encrypted values to check them function limitStringSize($str, $amount = 20) {
if (strlen($str) > $amount)
$str = substr($str, 0, $amount) . '...';
return $str;
}
echo "Encrypted: " . $encryptedData; $kpc = new kpcrypt($config['key']);
echo "Key: " . $kpc->getKey() . " \n";
// Echo out the results
$encryptedData = $kpc->encryptData($config['input'], 'AES-256-CBC', TRUE);
echo "Encrypted: " . limitStringSize($encryptedData);
echo "\n"; echo "\n";
echo "Decrypted: " . decryptData($encryptedData, $key); echo "Decrypted: " . limitStringSize($kpc->decryptData($encryptedData));
echo "\n"; echo "\n";
echo "Peak Memory: " . memory_get_peak_usage() / 1024 . "Kb"; // Check the memory in kb echo "Peak Memory: " . memory_get_peak_usage() / 1024 / 1024 . "Mb"; // Check the memory in kb
echo "\n\n";
// Calculate the string size increse
$inputSize = strlen($config['input']); // Bytes
$outputSize = strlen($encryptedData); // Bytes
// Echo out sizing information
echo "Sizeof input: " . strlen($config['input']) . "\n"; // Bytes
echo "Sizeof output:" . strlen($encryptedData) . "\n"; // Bytes
echo "\n";
```
#### File Encryption
```php
<?php
require("./lib.php");
/**
* This is a simple example of how to encrypt files with the library
* The following code will read the file in chunks instead of loading
* the whole thing in memory, trying to keep the "Peak Memory Usage"
* as low as possible.
*/
define("INPUT_FILE", "./lib.php");
define("OUTPUT_FILE", "./testdir/" . "./example.enc.php");
define("DEC_OUTPUT_FILE", "./testdir/" . "./example.dec.php");
// Make sure the testing folder exists
if ( !file_exists("./testdir") ) {
mkdir("./testdir", 0777, true);
}
// Initialize the class
$lib = new kpcrypt();
// $lib->setEncryptionBlocks(128); // This will take ~10 times more time, but will use half as much memory.
// Really the best value is the default one.
$enc_start = round(microtime(true) * 1000);
// Encrypt the file
$lib->encryptFile(INPUT_FILE, OUTPUT_FILE);
$enc_end = round(microtime(true) * 1000) - $enc_start;
$dec_start = round(microtime(true) * 1000);
// Decrypt the file as well
$lib->decryptFile(OUTPUT_FILE, DEC_OUTPUT_FILE);
$dec_end = round(microtime(true) * 1000) - $dec_start;
/**
* NOTE: You can also compress the output using gzip
*/
echo "\n\n";
echo "Input File Size: " . round(filesize(INPUT_FILE) / 1024 / 1024, 2) . "Mb";
echo "\n";
echo "Output File Size: " . round(filesize(OUTPUT_FILE) / 1024 / 1024, 2) . "Mb";
echo "\n";
echo "Peak Memory: " . memory_get_peak_usage() / 1024 / 1024 . "Mb"; // Check the memory in Mb
echo "\n";
echo "Encryption Time: $enc_end ms\n"; // 9700k = ~210ms ( 4Mb = 100ms )
echo "Decryption Time: $dec_end ms\n";
echo "\n";
$errors = $lib->getErrors();
echo "Errors: " . ( !empty($errors) ? "\n" . json_encode($errors, JSON_PRETTY_PRINT) : "No Errors!");
``` ```
@ -29,4 +118,4 @@ echo "Peak Memory: " . memory_get_peak_usage() / 1024 . "Kb"; // Check the memor
The licensing of the project is the [MIT license](https://mit-license.org/) The licensing of the project is the [MIT license](https://mit-license.org/)
#### Credit #### Credit
Icons made by [monkik](https://www.flaticon.com/authors/monkik) from [www.flaticon.com](https://www.flaticon.com/); Repository icon made by [monkik](https://www.flaticon.com/authors/monkik) from [www.flaticon.com](https://www.flaticon.com/);

56
example-file.php Normal file
View File

@ -0,0 +1,56 @@
<?php
require("./lib.php");
/**
* This is a simple example of how to encrypt files with the library
* The following code will read the file in chunks instead of loading
* the whole thing in memory, trying to keep the "Peak Memory Usage"
* as low as possible.
*/
define("INPUT_FILE", "./lib.php");
define("OUTPUT_FILE", "./testdir/" . "./example.enc.php");
define("DEC_OUTPUT_FILE", "./testdir/" . "./example.dec.php");
// Make sure the testing folder exists
if ( !file_exists("./testdir") ) {
mkdir("./testdir", 0777, true);
}
// Initialize the class
$lib = new kpcrypt();
// $lib->setEncryptionBlocks(128); // This will take ~10 times more time, but will use half as much memory.
// Really the best value is the default one.
$enc_start = round(microtime(true) * 1000);
// Encrypt the file
$lib->encryptFile(INPUT_FILE, OUTPUT_FILE);
$enc_end = round(microtime(true) * 1000) - $enc_start;
$dec_start = round(microtime(true) * 1000);
// Decrypt the file as well
$lib->decryptFile(OUTPUT_FILE, DEC_OUTPUT_FILE);
$dec_end = round(microtime(true) * 1000) - $dec_start;
/**
* NOTE: You can also compress the output using gzip
*/
echo "\n\n";
echo "Input File Size: " . round(filesize(INPUT_FILE) / 1024 / 1024, 2) . "Mb";
echo "\n";
echo "Output File Size: " . round(filesize(OUTPUT_FILE) / 1024 / 1024, 2) . "Mb";
echo "\n";
echo "Peak Memory: " . memory_get_peak_usage() / 1024 / 1024 . "Mb"; // Check the memory in Mb
echo "\n";
echo "Encryption Time: $enc_end ms\n"; // 9700k = ~210ms ( 4Mb = 100ms )
echo "Decryption Time: $dec_end ms\n";
echo "\n";
$errors = $lib->getErrors();
echo "Errors: " . ( !empty($errors) ? "\n" . json_encode($errors, JSON_PRETTY_PRINT) : "No Errors!");

View File

@ -8,5 +8,46 @@ if ( empty($config) || empty($config['key']) || empty($config['input']) ) {
exit(1); exit(1);
} }
ini_set('memory_limit','1G');
function limitStringSize($str, $amount = 20) {
if (strlen($str) > $amount)
$str = substr($str, 0, $amount) . '...';
return $str;
}
$kpc = new kpcrypt($config['key']);
echo "Key: " . $kpc->getKey() . " \n";
$enc_start = round(microtime(true) * 1000);
// Echo out the results // Echo out the results
echo $encryptedData = encryptData($config['input'], $config['key'], 'AES-256-CBC', TRUE); $encryptedData = $kpc->encryptData($config['input'], 'AES-256-CBC', TRUE);
$enc_end = round(microtime(true) * 1000) - $enc_start;
$dec_start = round(microtime(true) * 1000);
echo "Encrypted: " . limitStringSize($encryptedData);
echo "\n";
echo "Decrypted: " . limitStringSize($kpc->decryptData($encryptedData));
$dec_end = round(microtime(true) * 1000) - $dec_start;
echo "\n";
echo "Peak Memory: " . memory_get_peak_usage() / 1024 / 1024 . "Mb"; // Check the memory in kb
echo "\n\n";
// Calculate the string size increse
$inputSize = strlen($config['input']); // Bytes
$outputSize = strlen($encryptedData); // Bytes
// Echo out sizing information
echo "Sizeof input: " . strlen($config['input']) . "\n"; // Bytes
echo "Sizeof output:" . strlen($encryptedData) . "\n"; // Bytes
echo "\n";
echo "Encryption Time: $enc_end ms\n"; // 9700k = ~210ms
echo "\n";
echo "Decryption Time: $dec_end ms\n";
echo "\n";

223
lib.php
View File

@ -1,26 +1,109 @@
<?php <?php
/** /**
* @param string $data The data to be encrypted, this can only encrypt strings. * @author Kato Twofold
* @param string $key The key to use to encrypt the data, this key should be generated using the openssl_random_pseudo_bytes * @copyright MIT
* @param string $cipherMethod The cypher method to use in the encryption process, these can be checked using *
* @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. * 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.
*
*/ */
function encryptData( string $data, string $key, string $cipherMethod = 'AES-256-CBC', bool $integrity_check = TRUE) { 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 // Lowercase the cipher
$cipherMethod = strtolower($cipherMethod); $this->cipherMethod = strtolower($cipherMethod);
// Check if the encryption method is valid // Check if the encryption method is valid
if (!in_array($cipherMethod, openssl_get_cipher_methods())) { if (!in_array($this->cipherMethod, openssl_get_cipher_methods())) {
// Cypher was not in the available ciphers list. // Cypher was not in the available ciphers list.
$this->errorLog[] = "[" . __LINE__ . "]" . $this->cipherMethod . " isn't a valid cipherMethod.";
return FALSE; 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 // To encrypt a string
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($cipherMethod)); $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($this->cipherMethod));
// Encrypt the data // Encrypt the data
$encryptedData = openssl_encrypt($data, $cipherMethod, $key, OPENSSL_RAW_DATA, $iv); $encryptedData = openssl_encrypt($data, $this->cipherMethod, $key, OPENSSL_RAW_DATA, $iv);
// Append the iv to the encrypted data // Append the iv to the encrypted data
$encryptedData = $iv . $encryptedData; $encryptedData = $iv . $encryptedData;
@ -31,41 +114,32 @@ function encryptData( string $data, string $key, string $cipherMethod = 'AES-256
// Check if we should verify the integrity of the encryption // Check if we should verify the integrity of the encryption
if ($integrity_check === TRUE) { if ($integrity_check === TRUE) {
// Test for decryption validity // Test for decryption validity
if ( md5(decryptData($encryptedData, $key, $cipherMethod)) === md5($data) ) { if (md5($this->decryptData($encryptedData)) === md5($data)) {
// Put the output in the result // Put the output in the result
return $encryptedData; return $encryptedData;
} else { } else {
// Assign the output to the result // Assign the output to the result
$this->errorLog[] = "[" . __LINE__ . "]" . "Failed integrity check";
return FALSE; return FALSE;
} }
} else { } else {
// Simply return the results // Simply return the results
return $encryptedData; return $encryptedData;
} }
} }
/** /**
* @param string $data The data to be encrypted, this can only encrypt strings. * @param string $data The data to be encrypted, this can only encrypt strings.
* @param string $key The key to use to encrypt the data, this key should be generated using the openssl_random_pseudo_bytes
* @param string $cipherMethod The cypher method to use in the encryption process, these can be checked using
*/ */
function decryptData( string $data, string $key, string $cipherMethod = 'AES-256-CBC' ) { function decryptData(string $data) {
// Grab the key from self-reference
// Lowercase the cipher $key = $this->key;
$cipherMethod = strtolower($cipherMethod);
// Check if the encryption method is valid
if (!in_array($cipherMethod, openssl_get_cipher_methods())) {
// Cypher was not in the available ciphers list.
return FALSE;
}
// base64 decode the data. // base64 decode the data.
$data = base64_decode($data); $data = base64_decode($data);
// Get the length of the IV // Get the length of the IV
$iv_length = openssl_cipher_iv_length($cipherMethod); $iv_length = openssl_cipher_iv_length($this->cipherMethod);
// Get the IV from the decoded data // Get the IV from the decoded data
$iv = substr($data, 0, $iv_length); $iv = substr($data, 0, $iv_length);
@ -73,8 +147,107 @@ function decryptData( string $data, string $key, string $cipherMethod = 'AES-256
$data = substr($data, $iv_length); $data = substr($data, $iv_length);
// Decrypt the data // Decrypt the data
$data = openssl_decrypt($data,$cipherMethod,$key, OPENSSL_RAW_DATA, $iv); $data = openssl_decrypt($data, $this->cipherMethod, $key, OPENSSL_RAW_DATA, $iv);
// Return the data // Return the data
return $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
}