PHP Classes

File: src/PasswordLock.php

Recommend this page to a friend!
  Classes of Scott Arciszewski   PHP Password Lock   src/PasswordLock.php   Download  
File: src/PasswordLock.php
Role: Class source
Content type: text/plain
Description: Class source
Class: PHP Password Lock
Hash and encrypt passwords with Bcrypt and SHA2
Author: By
Last change:
Date: 5 years ago
Size: 3,836 bytes
 

Contents

Class file image Download
<?php
declare(strict_types=1);
namespace
ParagonIE\PasswordLock;

use \
Defuse\Crypto\Crypto;
use \
Defuse\Crypto\Key;
use \
ParagonIE\ConstantTime\Base64;
use \
ParagonIE\ConstantTime\Binary;

class
PasswordLock
{
   
/**
     * 1. Hash password using bcrypt-base64-SHA256
     * 2. Encrypt-then-MAC the hash
     *
     * @param string $password
     * @param Key $aesKey
     * @return string
     * @throws \Exception
     * @throws \InvalidArgumentException
     */
   
public static function hashAndEncrypt(string $password, Key $aesKey): string
   
{
       
/** @var string $hash */
       
$hash = \password_hash(
           
Base64::encode(
                \
hash('sha384', $password, true)
            ),
           
PASSWORD_DEFAULT
       
);
        if (!\
is_string($hash)) {
            throw new \
Exception("Unknown hashing error.");
        }
        return
Crypto::encrypt($hash, $aesKey);
    }
   
/**
     * 1. VerifyHMAC-then-Decrypt the ciphertext to get the hash
     * 2. Verify that the password matches the hash
     *
     * @param string $password
     * @param string $ciphertext
     * @param string $aesKey - must be exactly 16 bytes
     * @return bool
     * @throws \Exception
     * @throws \InvalidArgumentException
     */
   
public static function decryptAndVerifyLegacy(string $password, string $ciphertext, string $aesKey): bool
   
{
        if (
Binary::safeStrlen($aesKey) !== 16) {
            throw new \
Exception("Encryption keys must be 16 bytes long");
        }
       
$hash = Crypto::legacyDecrypt(
           
$ciphertext,
           
$aesKey
       
);
        if (!\
is_string($hash)) {
            throw new \
Exception("Unknown hashing error.");
        }
        return \
password_verify(
           
Base64::encode(
                \
hash('sha256', $password, true)
            ),
           
$hash
       
);
    }

   
/**
     * 1. VerifyHMAC-then-Decrypt the ciphertext to get the hash
     * 2. Verify that the password matches the hash
     *
     * @param string $password
     * @param string $ciphertext
     * @param Key $aesKey
     * @return bool
     * @throws \Exception
     * @throws \InvalidArgumentException
     */
   
public static function decryptAndVerify(string $password, string $ciphertext, Key $aesKey): bool
   
{
       
$hash = Crypto::decrypt(
           
$ciphertext,
           
$aesKey
       
);
        if (!\
is_string($hash)) {
            throw new \
Exception("Unknown hashing error.");
        }
        return \
password_verify(
           
Base64::encode(
                \
hash('sha384', $password, true)
            ),
           
$hash
       
);
    }

   
/**
     * Key rotation method -- decrypt with your old key then re-encrypt with your new key
     *
     * @param string $ciphertext
     * @param Key $oldKey
     * @param Key $newKey
     * @return string
     */
   
public static function rotateKey(string $ciphertext, Key $oldKey, Key $newKey): string
   
{
       
$plaintext = Crypto::decrypt($ciphertext, $oldKey);
        return
Crypto::encrypt($plaintext, $newKey);
    }

   
/**
     * For migrating from an older version of the library
     *
     * @param string $password
     * @param string $ciphertext
     * @param string $oldKey
     * @param Key $newKey
     * @return string
     * @throws \Exception
     */
   
public static function upgradeFromVersion1(
       
string $password,
       
string $ciphertext,
       
string $oldKey,
       
Key $newKey
   
): string {
        if (!
self::decryptAndVerifyLegacy($password, $ciphertext, $oldKey)) {
            throw new \
Exception(
               
'The correct password is necessary for legacy migration.'
           
);
        }
       
$plaintext = Crypto::legacyDecrypt($ciphertext, $oldKey);
        return
self::hashAndEncrypt($plaintext, $newKey);
    }
}