You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
245 lines
7.8 KiB
245 lines
7.8 KiB
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
/**
|
|
* @author Robert Strutts <Robert@TryingToScale.com>
|
|
* @copyright Copyright (c) 2022, Robert Strutts.
|
|
* @license https://mit-license.org/
|
|
*/
|
|
|
|
namespace tts\services;
|
|
|
|
/*
|
|
* NOTICE: This file is just for PLAY, not for PRODUCTION system!
|
|
*
|
|
* var_dump($enc->list_ssl_methods());
|
|
* var_dump($enc->list_hashes());
|
|
*/
|
|
|
|
final class encryption {
|
|
const iterations = 81952; // should be over 80K. The number of internal iterations to perform for the derivation.
|
|
const length = 64; // The length of the output string. If raw_output is TRUE this corresponds to the byte-length of the derived key, if raw_output is FALSE this corresponds to twice the byte-length of the derived key (as every byte of the key is returned as two hexits).
|
|
const raw = true; // When set to TRUE, outputs raw binary data. FALSE outputs lowercase hexits.
|
|
const key_bits = 32; // 256 bit encryption key
|
|
|
|
private $binary = false;
|
|
private $url_encode = false;
|
|
private $method = 'AES-256-CBC';
|
|
private $default_hash = 'sha256'; // should be sha256 or higher
|
|
private $_iterations = self::iterations, $_length=self::length, $_raw=self::raw, $_key_bits=self::key_bits;
|
|
|
|
public function change_security_level(string $level): bool {
|
|
switch (strtolower($level)) {
|
|
case 'blaze':
|
|
$this->set_loops(1843);
|
|
$this->set_length(32);
|
|
$this->set_key_bits(16);
|
|
$this->method = 'blowfish';
|
|
$this->default_hash = 'md5';
|
|
return true;
|
|
case 'normal':
|
|
$this->set_loops(81952);
|
|
$this->set_length(64);
|
|
$this->set_key_bits(32);
|
|
$this->method = 'AES-256-CBC';
|
|
$this->default_hash = 'sha256';
|
|
return true;
|
|
case 'max':
|
|
case 'high':
|
|
case 'slow-secure':
|
|
$this->set_loops(77891);
|
|
$this->set_length(128);
|
|
$this->set_key_bits(64);
|
|
$this->method = 'AES-256-XTS';
|
|
$this->default_hash = 'sha512';
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public function set_loops(int $iterations=self::iterations): void {
|
|
$this->_iterations = $iterations;
|
|
}
|
|
|
|
public function set_length(int $length=self::length): void {
|
|
$this->_length = $length;
|
|
}
|
|
|
|
public function set_raw_output(bool $output_raw=self::raw): void {
|
|
$this->_raw = $output_raw;
|
|
}
|
|
|
|
public function set_key_bits(int $key_bits=self::key_bits): void {
|
|
$this->_key_bits = $key_bits;
|
|
}
|
|
|
|
public function list_ssl_methods(): array {
|
|
$ciphers = openssl_get_cipher_methods();
|
|
|
|
//ECB mode should be avoided
|
|
$ciphers = array_filter( $ciphers, function($n) { return stripos($n,"ecb")===FALSE; } );
|
|
|
|
//At least as early as Aug 2016, Openssl declared the following weak: RC2, RC4, DES, 3DES, MD5 based
|
|
$ciphers = array_filter( $ciphers, function($c) { return stripos($c,"des")===FALSE; } );
|
|
$ciphers = array_filter( $ciphers, function($c) { return stripos($c,"rc2")===FALSE; } );
|
|
$ciphers = array_filter( $ciphers, function($c) { return stripos($c,"rc4")===FALSE; } );
|
|
$ciphers = array_filter( $ciphers, function($c) { return stripos($c,"md5")===FALSE; } );
|
|
|
|
$ciphers = array_map('strtoupper', $ciphers);
|
|
return array_unique($ciphers);
|
|
}
|
|
|
|
/**
|
|
* Change OpenSSl Security Level
|
|
* @param string $level (low, medium, medium-high, high) or algorithm Method name
|
|
*/
|
|
public function change_openssl(string $level): bool {
|
|
switch (strtolower($level)) {
|
|
case 'low':
|
|
case 'blowfish':
|
|
$this->method = 'blowfish';
|
|
return true;
|
|
case 'medium':
|
|
$this->method = 'AES-128-CBC';
|
|
return true;
|
|
case 'medium-high':
|
|
$this->method = 'AES-192-CBC';
|
|
return true;
|
|
case 'high':
|
|
$this->method = 'AES-256-CBC';
|
|
return true;
|
|
} // end of switch
|
|
|
|
if (in_array(strtoupper($level), $this->list_ssl_methods() )) {
|
|
$this->method = strtoupper($level);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public function list_hashes(): array {
|
|
return hash_algos();
|
|
}
|
|
|
|
public function change_hash(string $hash): bool {
|
|
if (in_array(strtolower($hash), $this->list_hashes() )) {
|
|
$this->default_hash = strtolower($hash);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public function set_binary_output(bool $bin) {
|
|
$this->binary = $bin;
|
|
}
|
|
|
|
public function set_url_encode(bool $url_encode) {
|
|
$this->url_encode = $url_encode;
|
|
}
|
|
|
|
/**
|
|
* OpenSSL Encrypt text with key
|
|
* @param string $key password
|
|
* @param string $text data
|
|
* @return string encoded text
|
|
*/
|
|
public function encrypt(string $key, string $text, bool $validate = true): string {
|
|
$key = ($validate) ? $this->get_valid_key($key) : $key;
|
|
$ivsize = openssl_cipher_iv_length($this->method);
|
|
$iv = random_bytes($ivsize); // Requires PHP 7
|
|
// Encryption key generated by PBKDF2 (since PHP 5.5)
|
|
$keys = hash_pbkdf2($this->default_hash, $key, $iv, $this->_iterations, $this->_length, $this->_raw);
|
|
$encKey = substr($keys, 0, $this->_key_bits); // X bit encryption key
|
|
$hmacKey = substr($keys, $this->_key_bits); // X bit hmac key
|
|
$ciphertext = openssl_encrypt(
|
|
$text,
|
|
$this->method,
|
|
$encKey,
|
|
OPENSSL_RAW_DATA,
|
|
$iv
|
|
);
|
|
$hmac = hash_hmac($this->default_hash, $iv . $ciphertext, $hmacKey);
|
|
if (! $this->binary) {
|
|
return (! $this->url_encode) ? base64_encode($hmac . $iv . $ciphertext) : px_base64url_encode($hmac . $iv . $ciphertext);
|
|
} else {
|
|
return $hmac . $iv . $ciphertext;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* OpenSSL Decrypt data with key
|
|
* @param string $key password
|
|
* @param string $data encoded text
|
|
* @return string plain text
|
|
*/
|
|
public function decrypt(string $key, string $data, bool $validate = true): string {
|
|
$key = ($validate) ? $this->get_valid_key($key) : $key;
|
|
if (! $this->binary) {
|
|
$text = (! $this->url_encode) ? base64_decode($data) : px_base64url_decode($data);
|
|
} else {
|
|
$text = $data;
|
|
}
|
|
$hmac = substr($text, 0, $this->_length);
|
|
$ivsize = openssl_cipher_iv_length($this->method);
|
|
$iv = (! $this->binary) ? substr($text, $this->_length, $ivsize) : $ivsize;
|
|
$ciphertext = substr($text, $ivsize + $this->_length);
|
|
// Generate the encryption and hmac keys
|
|
$keys = hash_pbkdf2($this->default_hash, $key, $iv, $this->_iterations, $this->_length, $this->_raw);
|
|
$encKey = substr($keys, 0, $this->_key_bits); // X bit encryption key
|
|
$hmacNew = hash_hmac($this->default_hash, $iv . $ciphertext, substr($keys, $this->_key_bits));
|
|
if (! hash_equals($hmac, $hmacNew)) { // to prevent timing attacks
|
|
return 'FALSE'; // Note: hash_equals() requires PHP5.6+
|
|
}
|
|
return openssl_decrypt(
|
|
$ciphertext,
|
|
$this->method,
|
|
$encKey,
|
|
OPENSSL_RAW_DATA,
|
|
$iv
|
|
);
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Try to make sure you have a valid key
|
|
* @param string $key as input
|
|
* @return string new key
|
|
* @throws Exception
|
|
*/
|
|
public function get_valid_key(string $key): string {
|
|
$ivsize = openssl_cipher_iv_length($this->method);
|
|
$key = substr($key, 0, $ivsize * 2);
|
|
$keysize = strlen($key);
|
|
|
|
if ($keysize != $ivsize * 2) {
|
|
throw new \Exception('Unable to use ENC key: Bad key size!');
|
|
}
|
|
|
|
if (! ctype_xdigit($key)) {
|
|
throw new \Exception('Unable to use ENC key: None HEX Digits!');
|
|
}
|
|
|
|
return bin2hex(pack('H*', $key));
|
|
}
|
|
|
|
/**
|
|
* Make a new Secure key
|
|
* @return string key
|
|
*/
|
|
public function generate_valid_key(): string {
|
|
$ivsize = openssl_cipher_iv_length($this->method);
|
|
return bin2hex(random_bytes($ivsize));
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
* INSERT:
|
|
INSERT INTO users (username, password) VALUES ('root', AES_ENCRYPT('somepassword', 'key12346123'));
|
|
and SELECT:
|
|
|
|
SELECT AES_DECRYPT(password, 'key12346123') AS pwd FROM users WHERE username = 'root';
|
|
Also, this requires SSL connection to the database.
|
|
*/
|
|
|