Initial
This commit is contained in:
84
system/Encryption/Handlers/BaseHandler.php
Normal file
84
system/Encryption/Handlers/BaseHandler.php
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CodeIgniter 4 framework.
|
||||
*
|
||||
* (c) CodeIgniter Foundation <admin@codeigniter.com>
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Encryption\Handlers;
|
||||
|
||||
use CodeIgniter\Encryption\EncrypterInterface;
|
||||
use Config\Encryption;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* Base class for encryption handling
|
||||
*/
|
||||
abstract class BaseHandler implements EncrypterInterface
|
||||
{
|
||||
/**
|
||||
* Logger instance to record error messages and warnings.
|
||||
*
|
||||
* @var LoggerInterface
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct(?Encryption $config = null)
|
||||
{
|
||||
$config = $config ?? config('Encryption');
|
||||
|
||||
// make the parameters conveniently accessible
|
||||
foreach (get_object_vars($config) as $key => $value) {
|
||||
if (property_exists($this, $key)) {
|
||||
$this->{$key} = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Byte-safe substr()
|
||||
*
|
||||
* @param string $str
|
||||
* @param int $start
|
||||
* @param int $length
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function substr($str, $start, $length = null)
|
||||
{
|
||||
return mb_substr($str, $start, $length, '8bit');
|
||||
}
|
||||
|
||||
/**
|
||||
* __get() magic, providing readonly access to some of our properties
|
||||
*
|
||||
* @param string $key Property name
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get($key)
|
||||
{
|
||||
if ($this->__isset($key)) {
|
||||
return $this->{$key};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* __isset() magic, providing checking for some of our properties
|
||||
*
|
||||
* @param string $key Property name
|
||||
*/
|
||||
public function __isset($key): bool
|
||||
{
|
||||
return property_exists($this, $key);
|
||||
}
|
||||
}
|
||||
110
system/Encryption/Handlers/OpenSSLHandler.php
Normal file
110
system/Encryption/Handlers/OpenSSLHandler.php
Normal file
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CodeIgniter 4 framework.
|
||||
*
|
||||
* (c) CodeIgniter Foundation <admin@codeigniter.com>
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Encryption\Handlers;
|
||||
|
||||
use CodeIgniter\Encryption\Exceptions\EncryptionException;
|
||||
|
||||
/**
|
||||
* Encryption handling for OpenSSL library
|
||||
*/
|
||||
class OpenSSLHandler extends BaseHandler
|
||||
{
|
||||
/**
|
||||
* HMAC digest to use
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $digest = 'SHA512';
|
||||
|
||||
/**
|
||||
* Cipher to use
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $cipher = 'AES-256-CTR';
|
||||
|
||||
/**
|
||||
* Starter key
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $key = '';
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function encrypt($data, $params = null)
|
||||
{
|
||||
// Allow key override
|
||||
if ($params) {
|
||||
$this->key = is_array($params) && isset($params['key']) ? $params['key'] : $params;
|
||||
}
|
||||
|
||||
if (empty($this->key)) {
|
||||
throw EncryptionException::forNeedsStarterKey();
|
||||
}
|
||||
|
||||
// derive a secret key
|
||||
$secret = \hash_hkdf($this->digest, $this->key);
|
||||
|
||||
// basic encryption
|
||||
$iv = ($ivSize = \openssl_cipher_iv_length($this->cipher)) ? \openssl_random_pseudo_bytes($ivSize) : null;
|
||||
|
||||
$data = \openssl_encrypt($data, $this->cipher, $secret, OPENSSL_RAW_DATA, $iv);
|
||||
|
||||
if ($data === false) {
|
||||
throw EncryptionException::forEncryptionFailed();
|
||||
}
|
||||
|
||||
$result = $iv . $data;
|
||||
|
||||
$hmacKey = \hash_hmac($this->digest, $result, $secret, true);
|
||||
|
||||
return $hmacKey . $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function decrypt($data, $params = null)
|
||||
{
|
||||
// Allow key override
|
||||
if ($params) {
|
||||
$this->key = is_array($params) && isset($params['key']) ? $params['key'] : $params;
|
||||
}
|
||||
|
||||
if (empty($this->key)) {
|
||||
throw EncryptionException::forNeedsStarterKey();
|
||||
}
|
||||
|
||||
// derive a secret key
|
||||
$secret = \hash_hkdf($this->digest, $this->key);
|
||||
|
||||
$hmacLength = self::substr($this->digest, 3) / 8;
|
||||
$hmacKey = self::substr($data, 0, $hmacLength);
|
||||
$data = self::substr($data, $hmacLength);
|
||||
$hmacCalc = \hash_hmac($this->digest, $data, $secret, true);
|
||||
|
||||
if (! hash_equals($hmacKey, $hmacCalc)) {
|
||||
throw EncryptionException::forAuthenticationFailed();
|
||||
}
|
||||
|
||||
if ($ivSize = \openssl_cipher_iv_length($this->cipher)) {
|
||||
$iv = self::substr($data, 0, $ivSize);
|
||||
$data = self::substr($data, $ivSize);
|
||||
} else {
|
||||
$iv = null;
|
||||
}
|
||||
|
||||
return \openssl_decrypt($data, $this->cipher, $secret, OPENSSL_RAW_DATA, $iv);
|
||||
}
|
||||
}
|
||||
137
system/Encryption/Handlers/SodiumHandler.php
Normal file
137
system/Encryption/Handlers/SodiumHandler.php
Normal file
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CodeIgniter 4 framework.
|
||||
*
|
||||
* (c) CodeIgniter Foundation <admin@codeigniter.com>
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CodeIgniter\Encryption\Handlers;
|
||||
|
||||
use CodeIgniter\Encryption\Exceptions\EncryptionException;
|
||||
|
||||
/**
|
||||
* SodiumHandler uses libsodium in encryption.
|
||||
*
|
||||
* @see https://github.com/jedisct1/libsodium/issues/392
|
||||
*/
|
||||
class SodiumHandler extends BaseHandler
|
||||
{
|
||||
/**
|
||||
* Starter key
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $key = '';
|
||||
|
||||
/**
|
||||
* Block size for padding message.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $blockSize = 16;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function encrypt($data, $params = null)
|
||||
{
|
||||
$this->parseParams($params);
|
||||
|
||||
if (empty($this->key)) {
|
||||
throw EncryptionException::forNeedsStarterKey();
|
||||
}
|
||||
|
||||
// create a nonce for this operation
|
||||
$nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES); // 24 bytes
|
||||
|
||||
// add padding before we encrypt the data
|
||||
if ($this->blockSize <= 0) {
|
||||
throw EncryptionException::forEncryptionFailed();
|
||||
}
|
||||
|
||||
$data = sodium_pad($data, $this->blockSize);
|
||||
|
||||
// encrypt message and combine with nonce
|
||||
$ciphertext = $nonce . sodium_crypto_secretbox($data, $nonce, $this->key);
|
||||
|
||||
// cleanup buffers
|
||||
sodium_memzero($data);
|
||||
sodium_memzero($this->key);
|
||||
|
||||
return $ciphertext;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function decrypt($data, $params = null)
|
||||
{
|
||||
$this->parseParams($params);
|
||||
|
||||
if (empty($this->key)) {
|
||||
throw EncryptionException::forNeedsStarterKey();
|
||||
}
|
||||
|
||||
if (mb_strlen($data, '8bit') < (SODIUM_CRYPTO_SECRETBOX_NONCEBYTES + SODIUM_CRYPTO_SECRETBOX_MACBYTES)) {
|
||||
// message was truncated
|
||||
throw EncryptionException::forAuthenticationFailed();
|
||||
}
|
||||
|
||||
// Extract info from encrypted data
|
||||
$nonce = self::substr($data, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
|
||||
$ciphertext = self::substr($data, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
|
||||
|
||||
// decrypt data
|
||||
$data = sodium_crypto_secretbox_open($ciphertext, $nonce, $this->key);
|
||||
|
||||
if ($data === false) {
|
||||
// message was tampered in transit
|
||||
throw EncryptionException::forAuthenticationFailed(); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
// remove extra padding during encryption
|
||||
if ($this->blockSize <= 0) {
|
||||
throw EncryptionException::forAuthenticationFailed();
|
||||
}
|
||||
|
||||
$data = sodium_unpad($data, $this->blockSize);
|
||||
|
||||
// cleanup buffers
|
||||
sodium_memzero($ciphertext);
|
||||
sodium_memzero($this->key);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the $params before doing assignment.
|
||||
*
|
||||
* @param array|string|null $params
|
||||
*
|
||||
* @throws EncryptionException If key is empty
|
||||
*/
|
||||
protected function parseParams($params)
|
||||
{
|
||||
if ($params === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_array($params)) {
|
||||
if (isset($params['key'])) {
|
||||
$this->key = $params['key'];
|
||||
}
|
||||
|
||||
if (isset($params['blockSize'])) {
|
||||
$this->blockSize = $params['blockSize'];
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->key = (string) $params;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user