PHP Classes
elePHPant
Icontem

File: src/OTP/HOTP.php

Recommend this page to a friend!
  Classes of Scott Arciszewski  >  PHP Multi Factor Authentication  >  src/OTP/HOTP.php  >  Download  
File: src/OTP/HOTP.php
Role: Class source
Content type: text/plain
Description: Class source
Class: PHP Multi Factor Authentication
2 factor authentication independent of the vendor
Author: By
Last change:
Date: 1 month ago
Size: 2,780 bytes
 

 

Contents

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

use
ParagonIE\ConstantTime\{
   
Binary,
   
Hex
};
use
ParagonIE\HiddenString\HiddenString;

/**
 * Class HOTP
 * @package ParagonIE\MultiFactor\OTP
 */
class HOTP implements OTPInterface
{
   
/**
     * @var string
     */
   
protected $algo;

   
/**
     * @var int
     */
   
protected $length;

   
/**
     * HOTP constructor.
     *
     * @param int $length How many digits should each HOTP be?
     * @param string $algo Hash function to use
     */
   
public function __construct(
       
int $length = 6,
       
string $algo = 'sha1'
   
) {
       
$this->length = $length;
       
$this->algo = $algo;
    }

   
/**
     * Generate a HOTP secret in accordance with RFC 4226
     *
     * @ref https://tools.ietf.org/html/rfc4226
     * @param string|HiddenString $sharedSecret The key to use for determining the HOTP
     * @param int $counterValue Current time or HOTP counter
     * @return string
     * @throws \OutOfRangeException
     */
   
public function getCode(
       
$sharedSecret,
       
int $counterValue
   
): string {
        if (
$this->length < 1 || $this->length > 10) {
            throw new \
OutOfRangeException(
               
'Length must be between 1 and 10, as a consequence of RFC 6238.'
           
);
        }
       
$msg = $this->getTValue($counterValue, true);
       
$bytes = \hash_hmac($this->algo, $msg, is_string($sharedSecret) ? $sharedSecret : $sharedSecret->getString(), true);

       
$byteLen = Binary::safeStrlen($bytes);

       
// Per the RFC
       
$offset = \unpack('C', $bytes[$byteLen - 1])[1];
       
$offset &= 0x0f;

       
$unpacked = \array_values(
            \
unpack('C*', Binary::safeSubstr($bytes, $offset, 4))
        );

       
$intValue = (
            ((
$unpacked[0] & 0x7f) << 24)
            | ((
$unpacked[1] & 0xff) << 16)
            | ((
$unpacked[2] & 0xff) << 8)
            | ((
$unpacked[3] & 0xff) )
        );

       
$intValue %= 10 ** $this->length;

        return \
str_pad(
            (string)
$intValue,
           
$this->length,
           
'0',
            \
STR_PAD_LEFT
       
);
    }

   
/**
     * @return int
     */
   
public function getLength(): int
   
{
        return
$this->length;
    }

   
/**
     * Get the binary T value
     *
     * @param int $unixTimestamp
     * @param bool $rawOutput
     * @return string
     */
   
protected function getTValue(
       
int $counter,
       
bool $rawOutput = false
   
): string {
       
$hex = \str_pad(
            \
dechex($counter),
           
16,
           
'0',
           
STR_PAD_LEFT
       
);
        if (
$rawOutput) {
            return
Hex::decode($hex);
        }
        return
$hex;
    }
}