-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
5f5427a
commit 9be7156
Showing
9 changed files
with
376 additions
and
84 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
<?php | ||
declare(strict_types=1); | ||
namespace ParagonIE\MultiFactor\OTP; | ||
|
||
use ParagonIE\ConstantTime\{ | ||
Binary, | ||
Hex | ||
}; | ||
|
||
/** | ||
* Class HOTP | ||
* @package ParagonIE\MultiFactor\OTP | ||
*/ | ||
class HOTP | ||
{ | ||
/** | ||
* @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 $sharedSecret The key to use for determining the HOTP | ||
* @param int $counterValue Current time or HOTP counter | ||
* @return string | ||
* @throws \OutOfRangeException | ||
*/ | ||
public function getCode( | ||
string $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, $sharedSecret, 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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
<?php | ||
declare(strict_types=1); | ||
namespace ParagonIE\MultiFactor\OTP; | ||
|
||
/** | ||
* Interface OTPInterface | ||
* @package ParagonIE\MultiFactor\OTP | ||
*/ | ||
interface OTPInterface | ||
{ | ||
/** | ||
* Get the code we need | ||
* | ||
* @param string $sharedSecret The key to use for determining the TOTP | ||
* @param int $counterValue Current time or HOTP counter | ||
* @return string | ||
* @throws \OutOfRangeException | ||
*/ | ||
public function getCode( | ||
string $sharedSecret, | ||
int $counterValue | ||
): string; | ||
|
||
public function getLength(): int; | ||
} |
Oops, something went wrong.