Password Validator

<?php

  /**
   * Squiloople Framework
   *
   * LICENSE: Feel free to use and redistribute this code.
   *
   * @author Michael Rushton <michael@squiloople.com>
   * @link http://squiloople.com/
   * @category Squiloople
   * @package Models
   * @subpackage Validators
   * @version 1.0
   * @copyright © 2011 Michael Rushton
   */

  // Define the namespace
  namespace Models\Validators;

  /**
   * Password Validator
   *
   * Hash or validate passwords
   */
  final class PasswordValidator
  {

    /**
     * The password
     *
     * @access private
     * @var string $_password
     */
    private $_password;

    /**
     * The salt
     *
     * @access private
     * @var string $_salt
     */
    private $_salt;

    /**
     * The pepper
     *
     * @access private
     * @var string $_pepper
     */
    private $_pepper = 'Sz^3X6r[UyvV~2]_0stT}8 uY7RwZx4{q|Q91W5';

    /**
     * Set the password
     *
     * @access public
     * @param string $password
     */
    public function __construct($password = '')
    {
      $this->_password = $password;
    }

    /**
     * Call the constructor fluently
     *
     * @access public
     * @static
     * @param string $password
     * @return \Models\Validators\PasswordValidator
     */
    public static function setPassword($password)
    {
      return new self($password);
    }

    /**
     * Return the password
     *
     * @access public
     * @return string
     */
    public function getPassword()
    {
      return $this->_password;
    }

    /**
     * Create a random password
     *
     * @access public
     * @return \Models\Validators\PasswordValidator
     */
    public function randomizePassword()
    {

      // If the password is incorrectly formed then randomize again
      if (!$this->isValid($this->_password = substr($this->getSalt(true), 0, 8)))
      {
        $this->randomizePassword();
      }

      // Reset the salt
      $this->_salt = null;

      // Return the object
      return $this;

    }

    /**
     * Validate the password
     *
     * @access public
     * @param bool|string $new
     * @return bool
     */
    public function isValid($new = false)
    {

      // The password must contain at least one character of each case, one digit, and be between 8 and 39 characters inclusive in length
      if (!preg_match('/^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])[\x20-\x7E]{8,39}$/D', $new ?: $this->_password))
      {
        return false;
      }

      // Otherwise return true
      return true;

    }

    /**
     * Set the salt
     *
     * @access public
     * @param string $salt
     * @return \Models\Validators\PasswordValidator
     */
    public function setSalt($salt)
    {

      // Set the salt
      $this->_salt = $salt;

      // Return itself
      return $this;

    }

    /**
     * Generate and return a salt
     *
     * @access public
     * @param bool $reset
     * @return string
     */
    public function getSalt($reset = false)
    {

      // If a salt has been set and a reset is not required then return the stored salt
      if (!$reset && isset($this->_salt))
      {
        return $this->_salt;
      }

      // Reset the salt
      $salt = '';

      // Generate a random salt of 39 printable ASCII characters
      for ($i = 1; $i <= 39; ++$i)
      {
        $salt .= chr(mt_rand(32, 126));
      }

      // If the salt does not have the correct syntax then regenerate
      if (!$this->isValid($salt))
      {
        $this->getSalt(true);
      }

      // Return the random salt
      return $this->_salt = $salt;

    }

    /**
     * Return the pepper portion
     *
     * @access private
     * @return string
     */
    private function _getPepper()
    {
      return substr($this->_pepper, 0, 39 - strlen($this->_password));
    }

    /**
     * Hash the password
     *
     * @access private
     * @return string
     */
    private function _getHash()
    {
      return hash('sha384', $this->getSalt() . $this->_password . $this->_getPepper());
    }

    /**
     * Hash the password using an HMAC-inspired hash
     *
     * @access public
     * @return string
     */
    public function getHash()
    {
      return hash('sha512', substr(strrev($this->_pepper), 0, 20) . $this->_getHash());
    }

  }

To instantiate the password validator, either use the new keyword or call the class statically using \Models\Validators\PasswordValidator::setPassword() passing as the only parameter the password.

$passwordValidator = new \Models\Validators\PasswordValidator('password');

To validate the password, which must be between 8 and 39 characters inclusive and may only contain printable ASCII characters — at least one lower-case alphabetic character, one upper-case alphabetic character, and one digit — call the isValid() method. These methods accepts an optional parameter; if provided the parameter will be validated rather than the stored password.

$passwordValidator = \Models\Validators\PasswordValidator::setPassword('password');

$passwordValidator->isValid();           // Returns false
$passwordValidator->isValid('p4Ssw0rD'); // Returns true

To generate and store random valid password call the randomizePassword() method. This method does not accept any parameters.

$passwordValidator->randomizePassword();

To generate and return a salt, which is 39 characters in length and follows the same syntax rules as a valid password, call the getSalt() method. Generated salts are stored and returned if the method is called subsequent times. To generate a new salt pass a true parameter to the method.

$passwordValidator->getSalt(); // Returns h&%G8\SThz7\P"$j>nB[Fpip{_rS{(f{2DEw>4R
$passwordValidator->getSalt(); // Returns h&%G8\SThz7\P"$j>nB[Fpip{_rS{(f{2DEw>4R
$passwordValidator->getSalt(true) // Returns g0ow;+}5HnM*G ;|%!@?px$W4,DK)(3WbE*iK:1

To set an established salt, primarily used when verifying against an already hashed password, use the setSalt() method passing as the only parameter the salt. Calling getSalt() after setting a salt this way will return the set salt unless a true parameter is passed to the former.

$passwordValidator->setSalt('g0ow;+}5HnM*G ;|%!@?px$W4,DK)(3WbE*iK:1'); // Set the salt

To return a hash of the password, which automatically generates a salt if one has not already been set and appends to the password a portion of the pepper to increase its length to 39 characters, call the getHash() method. This uses an HMAC-inspired cryptographic hash function using SHA 384 for the first hash and SHA 512 for the second.

echo \Models\Validators\PasswordValidator::setPassword('p4$$W0rD')>getHash();

// Outputs: cc303db69078854d7d7501bc3df09c45a655daa60412d07747c25da98d612646ea5f8fb650fa8785f6b6ed8d350d6a4ae56343b3dd41e01ba2d93a163852b63c

Tags: ,

Saturday, February 19th, 2011 PHP

Leave a Reply