phpseclib/phpseclib/Crypt/RSA.php

885 lines
27 KiB
PHP

<?php
/**
* Pure-PHP PKCS#1 (v2.1) compliant implementation of RSA.
*
* PHP version 5
*
* Here's an example of how to encrypt and decrypt text with this library:
* <code>
* <?php
* include 'vendor/autoload.php';
*
* $private = \phpseclib3\Crypt\RSA::createKey();
* $public = $private->getPublicKey();
*
* $plaintext = 'terrafrost';
*
* $ciphertext = $public->encrypt($plaintext);
*
* echo $private->decrypt($ciphertext);
* ?>
* </code>
*
* Here's an example of how to create signatures and verify signatures with this library:
* <code>
* <?php
* include 'vendor/autoload.php';
*
* $private = \phpseclib3\Crypt\RSA::createKey();
* $public = $private->getPublicKey();
*
* $plaintext = 'terrafrost';
*
* $signature = $private->sign($plaintext);
*
* echo $public->verify($plaintext, $signature) ? 'verified' : 'unverified';
* ?>
* </code>
*
* One thing to consider when using this: so phpseclib uses PSS mode by default.
* Technically, id-RSASSA-PSS has a different key format than rsaEncryption. So
* should phpseclib save to the id-RSASSA-PSS format by default or the
* rsaEncryption format? For stand-alone keys I figure rsaEncryption is better
* because SSH doesn't use PSS and idk how many SSH servers would be able to
* decode an id-RSASSA-PSS key. For X.509 certificates the id-RSASSA-PSS
* format is used by default (unless you change it up to use PKCS1 instead)
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2009 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
declare(strict_types=1);
namespace phpseclib3\Crypt;
use phpseclib3\Crypt\Common\AsymmetricKey;
use phpseclib3\Crypt\RSA\Formats\Keys\PSS;
use phpseclib3\Crypt\RSA\PrivateKey;
use phpseclib3\Crypt\RSA\PublicKey;
use phpseclib3\Exception\InconsistentSetupException;
use phpseclib3\Exception\LengthException;
use phpseclib3\Exception\OutOfRangeException;
use phpseclib3\Exception\UnsupportedAlgorithmException;
use phpseclib3\Math\BigInteger;
/**
* Pure-PHP PKCS#1 compliant implementation of RSA.
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class RSA extends AsymmetricKey
{
/**
* Algorithm Name
*
* @var string
*/
public const ALGORITHM = 'RSA';
/**
* Use {@link http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding Optimal Asymmetric Encryption Padding}
* (OAEP) for encryption / decryption.
*
* Uses sha256 by default
*
* @see self::setHash()
* @see self::setMGFHash()
* @see self::encrypt()
* @see self::decrypt()
*/
public const ENCRYPTION_OAEP = 1;
/**
* Use PKCS#1 padding.
*
* Although self::PADDING_OAEP / self::PADDING_PSS offers more security, including PKCS#1 padding is necessary for purposes of backwards
* compatibility with protocols (like SSH-1) written before OAEP's introduction.
*
* @see self::encrypt()
* @see self::decrypt()
*/
public const ENCRYPTION_PKCS1 = 2;
/**
* Do not use any padding
*
* Although this method is not recommended it can none-the-less sometimes be useful if you're trying to decrypt some legacy
* stuff, if you're trying to diagnose why an encrypted message isn't decrypting, etc.
*
* @see self::encrypt()
* @see self::decrypt()
*/
public const ENCRYPTION_NONE = 4;
/**
* Use the Probabilistic Signature Scheme for signing
*
* Uses sha256 and 0 as the salt length
*
* @see self::setSaltLength()
* @see self::setMGFHash()
* @see self::setHash()
* @see self::sign()
* @see self::verify()
* @see self::setHash()
*/
public const SIGNATURE_PSS = 16;
/**
* Use a relaxed version of PKCS#1 padding for signature verification
*
* @see self::sign()
* @see self::verify()
* @see self::setHash()
*/
public const SIGNATURE_RELAXED_PKCS1 = 32;
/**
* Use PKCS#1 padding for signature verification
*
* @see self::sign()
* @see self::verify()
* @see self::setHash()
*/
public const SIGNATURE_PKCS1 = 64;
/**
* Encryption padding mode
*
* @var int
*/
protected $encryptionPadding = self::ENCRYPTION_OAEP;
/**
* Signature padding mode
*
* @var int
*/
protected $signaturePadding = self::SIGNATURE_PSS;
/**
* Length of hash function output
*
* @var int
*/
protected $hLen;
/**
* Length of salt
*
* @var int|null
*/
protected $sLen = null;
/**
* Label
*
* @var string
*/
protected $label = '';
/**
* Hash function for the Mask Generation Function
*
* @var Hash
*/
protected $mgfHash;
/**
* Length of MGF hash function output
*
* @var int
*/
protected $mgfHLen;
/**
* Modulus (ie. n)
*
* @var BigInteger
*/
protected $modulus;
/**
* Modulus length
*
* @var int
*/
protected $k;
/**
* Exponent (ie. e or d)
*
* @var BigInteger
*/
protected $exponent;
/**
* Default public exponent
*
* @var int
* @link http://en.wikipedia.org/wiki/65537_%28number%29
*/
private static $defaultExponent = 65537;
/**
* Enable Blinding?
*
* @var bool
*/
protected static $enableBlinding = true;
/**
* OpenSSL configuration file name.
*
* @see self::createKey()
* @var ?string
*/
protected static $configFile;
/**
* Smallest Prime
*
* Per <http://cseweb.ucsd.edu/~hovav/dist/survey.pdf#page=5>, this number ought not result in primes smaller
* than 256 bits. As a consequence if the key you're trying to create is 1024 bits and you've set smallestPrime
* to 384 bits then you're going to get a 384 bit prime and a 640 bit prime (384 + 1024 % 384). At least if
* engine is set to self::ENGINE_INTERNAL. If Engine is set to self::ENGINE_OPENSSL then smallest Prime is
* ignored (ie. multi-prime RSA support is more intended as a way to speed up RSA key generation when there's
* a chance neither gmp nor OpenSSL are installed)
*
* @var int
*/
private static $smallestPrime = 4096;
/**
* Public Exponent
*
* @var BigInteger
*/
protected $publicExponent;
/**
* Sets the public exponent for key generation
*
* This will be 65537 unless changed.
*/
public static function setExponent(int $val): void
{
self::$defaultExponent = $val;
}
/**
* Sets the smallest prime number in bits. Used for key generation
*
* This will be 4096 unless changed.
*/
public static function setSmallestPrime(int $val): void
{
self::$smallestPrime = $val;
}
/**
* Sets the OpenSSL config file path
*
* Set to the empty string to use the default config file
*/
public static function setOpenSSLConfigPath(string $val): void
{
self::$configFile = $val;
}
/**
* Create a private key
*
* The public key can be extracted from the private key
*/
public static function createKey(int $bits = 2048): PrivateKey
{
self::initialize_static_variables();
$class = new \ReflectionClass(static::class);
if ($class->isFinal()) {
throw new \RuntimeException('createKey() should not be called from final classes (' . static::class . ')');
}
$regSize = $bits >> 1; // divide by two to see how many bits P and Q would be
if ($regSize > self::$smallestPrime) {
$num_primes = floor($bits / self::$smallestPrime);
$regSize = self::$smallestPrime;
} else {
$num_primes = 2;
}
if ($num_primes == 2 && $bits >= 384 && self::$defaultExponent == 65537) {
if (!isset(self::$engines['PHP'])) {
self::useBestEngine();
}
// OpenSSL uses 65537 as the exponent and requires RSA keys be 384 bits minimum
if (self::$engines['OpenSSL']) {
$config = [];
if (self::$configFile) {
$config['config'] = self::$configFile;
}
$rsa = openssl_pkey_new(['private_key_bits' => $bits] + $config);
openssl_pkey_export($rsa, $privatekeystr, null, $config);
// clear the buffer of error strings stemming from a minimalistic openssl.cnf
while (openssl_error_string() !== false) {
}
return RSA::load($privatekeystr);
}
}
static $e;
if (!isset($e)) {
$e = new BigInteger(self::$defaultExponent);
}
$n = clone self::$one;
$exponents = $coefficients = $primes = [];
$lcm = [
'top' => clone self::$one,
'bottom' => false,
];
do {
for ($i = 1; $i <= $num_primes; $i++) {
if ($i != $num_primes) {
$primes[$i] = BigInteger::randomPrime($regSize);
} else {
extract(BigInteger::minMaxBits($bits));
/** @var BigInteger $min
* @var BigInteger $max
*/
[$min] = $min->divide($n);
$min = $min->add(self::$one);
[$max] = $max->divide($n);
$primes[$i] = BigInteger::randomRangePrime($min, $max);
}
// the first coefficient is calculated differently from the rest
// ie. instead of being $primes[1]->modInverse($primes[2]), it's $primes[2]->modInverse($primes[1])
if ($i > 2) {
$coefficients[$i] = $n->modInverse($primes[$i]);
}
$n = $n->multiply($primes[$i]);
$temp = $primes[$i]->subtract(self::$one);
// textbook RSA implementations use Euler's totient function instead of the least common multiple.
// see http://en.wikipedia.org/wiki/Euler%27s_totient_function
$lcm['top'] = $lcm['top']->multiply($temp);
$lcm['bottom'] = $lcm['bottom'] === false ? $temp : $lcm['bottom']->gcd($temp);
}
[$temp] = $lcm['top']->divide($lcm['bottom']);
$gcd = $temp->gcd($e);
$i0 = 1;
} while (!$gcd->equals(self::$one));
$coefficients[2] = $primes[2]->modInverse($primes[1]);
$d = $e->modInverse($temp);
foreach ($primes as $i => $prime) {
$temp = $prime->subtract(self::$one);
$exponents[$i] = $e->modInverse($temp);
}
// from <http://tools.ietf.org/html/rfc3447#appendix-A.1.2>:
// RSAPrivateKey ::= SEQUENCE {
// version Version,
// modulus INTEGER, -- n
// publicExponent INTEGER, -- e
// privateExponent INTEGER, -- d
// prime1 INTEGER, -- p
// prime2 INTEGER, -- q
// exponent1 INTEGER, -- d mod (p-1)
// exponent2 INTEGER, -- d mod (q-1)
// coefficient INTEGER, -- (inverse of q) mod p
// otherPrimeInfos OtherPrimeInfos OPTIONAL
// }
$privatekey = new PrivateKey();
$privatekey->modulus = $n;
$privatekey->k = $bits >> 3;
$privatekey->publicExponent = $e;
$privatekey->exponent = $d;
$privatekey->primes = $primes;
$privatekey->exponents = $exponents;
$privatekey->coefficients = $coefficients;
/*
$publickey = new PublicKey;
$publickey->modulus = $n;
$publickey->k = $bits >> 3;
$publickey->exponent = $e;
$publickey->publicExponent = $e;
$publickey->isPublic = true;
*/
return $privatekey;
}
/**
* OnLoad Handler
*
* @return PrivateKey|PublicKey|RSA
*/
protected static function onLoad(array $components)
{
$key = $components['isPublicKey'] ?
new PublicKey() :
new PrivateKey();
$key->modulus = $components['modulus'];
$key->publicExponent = $components['publicExponent'];
$key->k = $key->modulus->getLengthInBytes();
if ($components['isPublicKey'] || !isset($components['privateExponent'])) {
$key->exponent = $key->publicExponent;
} else {
$key->privateExponent = $components['privateExponent'];
$key->exponent = $key->privateExponent;
$key->primes = $components['primes'];
$key->exponents = $components['exponents'];
$key->coefficients = $components['coefficients'];
}
if ($components['format'] == PSS::class) {
// in the X509 world RSA keys are assumed to use PKCS1 padding by default. only if the key is
// explicitly a PSS key is the use of PSS assumed. phpseclib does not work like this. phpseclib
// uses PSS padding by default. it assumes the more secure method by default and altho it provides
// for the less secure PKCS1 method you have to go out of your way to use it. this is consistent
// with the latest trends in crypto. libsodium (NaCl) is actually a little more extreme in that
// not only does it defaults to the most secure methods - it doesn't even let you choose less
// secure methods
//$key = $key->withPadding(self::SIGNATURE_PSS);
if (isset($components['hash'])) {
$key = $key->withHash($components['hash']);
}
if (isset($components['MGFHash'])) {
$key = $key->withMGFHash($components['MGFHash']);
}
if (isset($components['saltLength'])) {
$key = $key->withSaltLength($components['saltLength']);
}
}
return $key;
}
/**
* Initialize static variables
*/
protected static function initialize_static_variables(): void
{
if (!isset(self::$configFile)) {
self::$configFile = dirname(__FILE__) . '/../openssl.cnf';
}
parent::initialize_static_variables();
}
/**
* Constructor
*
* PublicKey and PrivateKey objects can only be created from abstract RSA class
*/
protected function __construct()
{
parent::__construct();
$this->hLen = $this->hash->getLengthInBytes();
$this->mgfHash = new Hash('sha256');
$this->mgfHLen = $this->mgfHash->getLengthInBytes();
}
/**
* Integer-to-Octet-String primitive
*
* See {@link http://tools.ietf.org/html/rfc3447#section-4.1 RFC3447#section-4.1}.
*/
protected function i2osp(BigInteger $x, int $xLen): string
{
$x = $x->toBytes();
if (strlen($x) > $xLen) {
throw new OutOfRangeException('Resultant string length out of range');
}
return str_pad($x, $xLen, chr(0), STR_PAD_LEFT);
}
/**
* Octet-String-to-Integer primitive
*
* See {@link http://tools.ietf.org/html/rfc3447#section-4.2 RFC3447#section-4.2}.
*/
protected function os2ip(string $x): BigInteger
{
return new BigInteger($x, 256);
}
/**
* EMSA-PKCS1-V1_5-ENCODE
*
* See {@link http://tools.ietf.org/html/rfc3447#section-9.2 RFC3447#section-9.2}.
*
* @throws LengthException if the intended encoded message length is too short
*/
protected function emsa_pkcs1_v1_5_encode(string $m, int $emLen): string
{
$h = $this->hash->hash($m);
// see http://tools.ietf.org/html/rfc3447#page-43
switch ($this->hash->getHash()) {
case 'md2':
$t = "\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x02\x05\x00\x04\x10";
break;
case 'md5':
$t = "\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05\x05\x00\x04\x10";
break;
case 'sha1':
$t = "\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14";
break;
case 'sha256':
$t = "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20";
break;
case 'sha384':
$t = "\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x05\x00\x04\x30";
break;
case 'sha512':
$t = "\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40";
break;
// from https://www.emc.com/collateral/white-papers/h11300-pkcs-1v2-2-rsa-cryptography-standard-wp.pdf#page=40
case 'sha224':
$t = "\x30\x2d\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x04\x05\x00\x04\x1c";
break;
case 'sha512/224':
$t = "\x30\x2d\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x05\x05\x00\x04\x1c";
break;
case 'sha512/256':
$t = "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x06\x05\x00\x04\x20";
}
$t .= $h;
$tLen = strlen($t);
if ($emLen < $tLen + 11) {
throw new LengthException('Intended encoded message length too short');
}
$ps = str_repeat(chr(0xFF), $emLen - $tLen - 3);
$em = "\0\1$ps\0$t";
return $em;
}
/**
* EMSA-PKCS1-V1_5-ENCODE (without NULL)
*
* Quoting https://tools.ietf.org/html/rfc8017#page-65,
*
* "The parameters field associated with id-sha1, id-sha224, id-sha256,
* id-sha384, id-sha512, id-sha512/224, and id-sha512/256 should
* generally be omitted, but if present, it shall have a value of type
* NULL"
*/
protected function emsa_pkcs1_v1_5_encode_without_null(string $m, int $emLen): string
{
$h = $this->hash->hash($m);
// see http://tools.ietf.org/html/rfc3447#page-43
switch ($this->hash->getHash()) {
case 'sha1':
$t = "\x30\x1f\x30\x07\x06\x05\x2b\x0e\x03\x02\x1a\x04\x14";
break;
case 'sha256':
$t = "\x30\x2f\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x04\x20";
break;
case 'sha384':
$t = "\x30\x3f\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x04\x30";
break;
case 'sha512':
$t = "\x30\x4f\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x04\x40";
break;
// from https://www.emc.com/collateral/white-papers/h11300-pkcs-1v2-2-rsa-cryptography-standard-wp.pdf#page=40
case 'sha224':
$t = "\x30\x2b\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x04\x04\x1c";
break;
case 'sha512/224':
$t = "\x30\x2b\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x05\x04\x1c";
break;
case 'sha512/256':
$t = "\x30\x2f\x30\x0b\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x06\x04\x20";
break;
default:
throw new UnsupportedAlgorithmException('md2 and md5 require NULLs');
}
$t .= $h;
$tLen = strlen($t);
if ($emLen < $tLen + 11) {
throw new LengthException('Intended encoded message length too short');
}
$ps = str_repeat(chr(0xFF), $emLen - $tLen - 3);
$em = "\0\1$ps\0$t";
return $em;
}
/**
* MGF1
*
* See {@link http://tools.ietf.org/html/rfc3447#appendix-B.2.1 RFC3447#appendix-B.2.1}.
*/
protected function mgf1(string $mgfSeed, int $maskLen): string
{
// if $maskLen would yield strings larger than 4GB, PKCS#1 suggests a "Mask too long" error be output.
$t = '';
$count = ceil($maskLen / $this->mgfHLen);
for ($i = 0; $i < $count; $i++) {
$c = pack('N', $i);
$t .= $this->mgfHash->hash($mgfSeed . $c);
}
return substr($t, 0, $maskLen);
}
/**
* Returns the key size
*
* More specifically, this returns the size of the modulo in bits.
*/
public function getLength(): int
{
return !isset($this->modulus) ? 0 : $this->modulus->getLength();
}
/**
* Determines which hashing function should be used
*
* Used with signature production / verification and (if the encryption mode is self::PADDING_OAEP) encryption and
* decryption.
*/
public function withHash(string $hash): RSA
{
$new = clone $this;
// \phpseclib3\Crypt\Hash supports algorithms that PKCS#1 doesn't support. md5-96 and sha1-96, for example.
switch (strtolower($hash)) {
case 'md2':
case 'md5':
case 'sha1':
case 'sha256':
case 'sha384':
case 'sha512':
case 'sha224':
case 'sha512/224':
case 'sha512/256':
$new->hash = new Hash($hash);
break;
default:
throw new UnsupportedAlgorithmException(
'The only supported hash algorithms are: md2, md5, sha1, sha256, sha384, sha512, sha224, sha512/224, sha512/256'
);
}
$new->hLen = $new->hash->getLengthInBytes();
return $new;
}
/**
* Determines which hashing function should be used for the mask generation function
*
* The mask generation function is used by self::PADDING_OAEP and self::PADDING_PSS and although it's
* best if Hash and MGFHash are set to the same thing this is not a requirement.
*/
public function withMGFHash(string $hash): RSA
{
$new = clone $this;
// \phpseclib3\Crypt\Hash supports algorithms that PKCS#1 doesn't support. md5-96 and sha1-96, for example.
switch (strtolower($hash)) {
case 'md2':
case 'md5':
case 'sha1':
case 'sha256':
case 'sha384':
case 'sha512':
case 'sha224':
case 'sha512/224':
case 'sha512/256':
$new->mgfHash = new Hash($hash);
break;
default:
throw new UnsupportedAlgorithmException(
'The only supported hash algorithms are: md2, md5, sha1, sha256, sha384, sha512, sha224, sha512/224, sha512/256'
);
}
$new->mgfHLen = $new->mgfHash->getLengthInBytes();
return $new;
}
/**
* Returns the MGF hash algorithm currently being used
*/
public function getMGFHash(): Hash
{
return clone $this->mgfHash;
}
/**
* Determines the salt length
*
* Used by RSA::PADDING_PSS
*
* To quote from {@link http://tools.ietf.org/html/rfc3447#page-38 RFC3447#page-38}:
*
* Typical salt lengths in octets are hLen (the length of the output
* of the hash function Hash) and 0.
*/
public function withSaltLength(?int $sLen): RSA
{
$new = clone $this;
$new->sLen = $sLen;
return $new;
}
/**
* Returns the salt length currently being used
*/
public function getSaltLength(): int
{
return $this->sLen !== null ? $this->sLen : $this->hLen;
}
/**
* Determines the label
*
* Used by RSA::PADDING_OAEP
*
* To quote from {@link http://tools.ietf.org/html/rfc3447#page-17 RFC3447#page-17}:
*
* Both the encryption and the decryption operations of RSAES-OAEP take
* the value of a label L as input. In this version of PKCS #1, L is
* the empty string; other uses of the label are outside the scope of
* this document.
*/
public function withLabel(string $label): RSA
{
$new = clone $this;
$new->label = $label;
return $new;
}
/**
* Returns the label currently being used
*/
public function getLabel(): string
{
return $this->label;
}
/**
* Determines the padding modes
*
* Example: $key->withPadding(RSA::ENCRYPTION_PKCS1 | RSA::SIGNATURE_PKCS1);
*/
public function withPadding(int $padding): RSA
{
$masks = [
self::ENCRYPTION_OAEP,
self::ENCRYPTION_PKCS1,
self::ENCRYPTION_NONE,
];
$numSelected = 0;
$selected = 0;
foreach ($masks as $mask) {
if ($padding & $mask) {
$selected = $mask;
$numSelected++;
}
}
if ($numSelected > 1) {
throw new InconsistentSetupException('Multiple encryption padding modes have been selected; at most only one should be selected');
}
$encryptionPadding = $selected;
$masks = [
self::SIGNATURE_PSS,
self::SIGNATURE_RELAXED_PKCS1,
self::SIGNATURE_PKCS1,
];
$numSelected = 0;
$selected = 0;
foreach ($masks as $mask) {
if ($padding & $mask) {
$selected = $mask;
$numSelected++;
}
}
if ($numSelected > 1) {
throw new InconsistentSetupException('Multiple signature padding modes have been selected; at most only one should be selected');
}
$signaturePadding = $selected;
$new = clone $this;
$new->encryptionPadding = $encryptionPadding;
$new->signaturePadding = $signaturePadding;
return $new;
}
/**
* Returns the padding currently being used
*/
public function getPadding(): int
{
return $this->signaturePadding | $this->encryptionPadding;
}
/**
* Returns the current engine being used
*
* OpenSSL is only used in this class (and it's subclasses) for key generation
* Even then it depends on the parameters you're using. It's not used for
* multi-prime RSA nor is it used if the key length is outside of the range
* supported by OpenSSL
*
* @see self::useInternalEngine()
* @see self::useBestEngine()
*/
public function getEngine(): string
{
if (!isset(self::$engines['PHP'])) {
self::useBestEngine();
}
return self::$engines['OpenSSL'] && self::$defaultExponent == 65537 ?
'OpenSSL' :
'PHP';
}
/**
* Enable RSA Blinding
*/
public static function enableBlinding(): void
{
static::$enableBlinding = true;
}
/**
* Disable RSA Blinding
*/
public static function disableBlinding(): void
{
static::$enableBlinding = false;
}
}