<?php 
declare(strict_types=1); 
namespace ParagonIE\Passwdqc; 
 
/** 
 * Class Params 
 * 
 * Not supported (due to no PAM integration): 
 * 
 * - random 
 * - enforce 
 * - retry 
 * 
 * @package ParagonIE\Passwdqc 
 */ 
class Params 
{ 
    /** 
     * The minimum allowed password lengths for different kinds of 
     * passwords/passphrases. 
     * 
     * @var int[] 
     */ 
    protected $min = [ 
        PHP_INT_MAX, 
        24, 
        11, 
        8, 
        7 
    ]; 
 
    /** 
     * The maximum allowed password length. 
     * 
     * This can be used to prevent users from setting passwords that may be too 
     * long for some system services. 
     * 
     * The value 8 is treated specially: if max is set to 8, passwords longer 
     * than 8 characters will not be rejected, but will be truncated to 8 
     * characters for the strength checks and the user will be warned. 
     * 
     * This is to be used with the traditional DES-based password hashes, 
     * which truncate the password at 8 characters. 
     * 
     * It is important that you do set max = 8 if you are using the traditional 
     * hashes, or some weak passwords will pass the checks. 
     * 
     * @var int 
     */ 
    protected $max = 40; 
 
    /** 
     * The number of words required for a passphrase, or 0 to disable the support 
     * for user-chosen passphrases. 
     * 
     * @var int 
     */ 
    protected $passphrase = 3; 
 
    /** 
     * The length of common substring required to conclude that a password is 
     * at least partially based on information found in a character string, 
     * or 0 to disable the substring search. 
     * 
     * Note that the password will not be rejected once a weak substring is 
     * found; it will instead be subjected to the usual strength requirements 
     * with the weak substring partially discounted. 
     * 
     * The substring search is case-insensitive and is able to detect and 
     * remove a common substring spelled backwards. 
     * 
     * @var int 
     */ 
    protected $match = 4; 
 
    /** 
     * Whether a new password is allowed to be similar to the old one. 
     * 
     * The passwords are considered to be similar when there is a sufficiently 
     * long common substring and the new password with the substring partially 
     * discounted would be weak. 
     * 
     * @var string ('permit', 'deny') 
     */ 
    protected $similar = 'deny'; 
 
    /** 
     * @return int 
     */ 
    public function getMatch(): int 
    { 
        return $this->match; 
    } 
 
    /** 
     * @return int 
     */ 
    public function getMax(): int 
    { 
        return $this->max; 
    } 
 
    /** 
     * @param int $offset 
     * @return int 
     */ 
    public function getMin(int $offset = 0): int 
    { 
        if ($offset < 0 || $offset > 4) { 
            throw new \RangeException('Offset is out of range. Must be between 0 and 4.'); 
        } 
        return $this->min[$offset]; 
    } 
 
    /** 
     * @return int 
     */ 
    public function getPassphrase(): int 
    { 
       return $this->passphrase; 
    } 
 
    /** 
     * @return string 
     */ 
    public function getSimilar(): string 
    { 
        return $this->similar; 
    } 
 
    /** 
     * @return bool 
     */ 
    public function getSimilarDeny(): bool 
    { 
        return $this->similar === 'deny'; 
    } 
 
    /** 
     * Set the minimum values in one go. 
     * 
     * @param array $min 
     * @return Params 
     * @throws \Exception 
     * @throws \RangeException 
     */ 
    public function setMin(array $min): self 
    { 
        $min = \array_values($min); 
        if (\count($min) !== 5) { 
            throw new \Exception('Minimum values must contain 5 elements'); 
        } 
        if (\count($min, \COUNT_RECURSIVE) !== 5) { 
            throw new \Exception('Minimum values must contain 5 elements'); 
        } 
        for ($i = 0; $i < 5; ++$i) { 
            if ($min[$i] === null) { 
                $min[$i] = PHP_INT_MAX; 
            } elseif (\is_numeric($min[$i])) { 
                $min[$i] = (int) $min[$i]; 
            } else { 
                throw new \Exception('Minimum values must only contain integers.'); 
            } 
            if ($min[$i] < 0) { 
                throw new \RangeException('Value must be a positive integer.'); 
            } 
            if ($i > 0 && $min[$i] < $min[$i - 1]) { 
                throw new \RangeException( 
                    'Each subsequent number is required to be no larger than the preceding one.' 
                ); 
            } 
        } 
        $this->min = $min; 
        return $this; 
    } 
 
    /** 
     * Sets a particular minimum value. 
     * 
     * @param int $value 
     * @param int $offset 
     * @return Params 
     * @throws \RangeException 
     */ 
    public function setMinValue(int $value, int $offset): self 
    { 
        if ($offset < 0 || $offset > 4) { 
            throw new \RangeException('Offset is out of range. Must be between 0 and 4.'); 
        } 
        if ($value < 0) { 
            throw new \RangeException('Value must be a positive integer.'); 
        } 
        $this->min[$offset] = $value; 
        return $this; 
    } 
 
    /** 
     * Sets the maximum length for passwords to be evaluated. 
     * 
     * @param int $max 
     * @return Params 
     */ 
    public function setMax(int $max): self 
    { 
        if ($max < 0) { 
            throw new \RangeException('Value must be a positive integer.'); 
        } 
        $this->max = $max; 
        return $this; 
    } 
 
    /** 
     * Set the minimum number of words in a passphrase. 
     * 
     * @param int $passphrase 
     * @return Params 
     */ 
    public function setPassphrase(int $passphrase): self 
    { 
        if ($passphrase < 0) { 
            throw new \RangeException('Value must be a positive integer.'); 
        } 
        $this->passphrase = $passphrase; 
        return $this; 
    } 
 
    /** 
     * @param int $match 
     * @return Params 
     */ 
    public function setMatch(int $match): self 
    { 
        if ($match < 0) { 
            throw new \RangeException('Value must be a positive integer.'); 
        } 
        $this->match = $match; 
        return $this; 
    } 
 
    /** 
     * @param string $similar 
     * @return Params 
     * @throws \Exception 
     */ 
    public function setSimilar(string $similar): self 
    { 
        switch ($similar) { 
            case 'permit': 
            case 'deny': 
                $this->similar = $similar; 
                break; 
            default: 
                throw new \Exception('Invalid similar parameter'); 
        } 
        return $this; 
    } 
} 
 
 |