Skip to content

Commit

Permalink
Port over the RFC reference tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ericmann committed Mar 2, 2018
1 parent 665b11f commit 9c7d057
Show file tree
Hide file tree
Showing 2 changed files with 159 additions and 2 deletions.
29 changes: 27 additions & 2 deletions providers/class.two-factor-totp.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,31 @@ protected function __construct() {
return parent::__construct();
}

/**
* Timestamp returned by time()
*
* @var int $now
*/
private static $now;

/**
* Override time() in the current object for testing.
*
* @return int
*/
private static function time() {
return self::$now ?: time();
}

/**
* Set up the internal state of time() invocations for deterministic generation.
*
* @param int $now Timestamp to use when overriding time().
*/
public static function __set_time( $now ) {
self::$now = $now;
}

/**
* Ensures only one instance of this class exists in memory at any one time.
*/
Expand Down Expand Up @@ -196,7 +221,7 @@ public static function is_valid_authcode( $key, $authcode, $hash = 'sha1', $time
$ticks = range( - $max_ticks, $max_ticks );
usort( $ticks, array( __CLASS__, 'abssort' ) );

$time = time() / self::DEFAULT_TIME_STEP_SEC;
$time = self::time() / self::DEFAULT_TIME_STEP_SEC;

$digits = strlen( $authcode );

Expand Down Expand Up @@ -312,7 +337,7 @@ public static function calc_totp( $key, $step_count = false, $digits = self::DEF
}

if ( false === $step_count ) {
$step_count = floor( time() / $time_step );
$step_count = floor( self::time() / $time_step );
}

$timestamp = self::pack64( $step_count );
Expand Down
132 changes: 132 additions & 0 deletions tests/providers/class.two-factor-totp.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,21 @@

class Tests_Two_Factor_Totp extends WP_UnitTestCase {

private static $token = '12345678901234567890';
private static $step = 30;

private static $vectors = [
59 => ['94287082', '46119246', '90693936'],
1111111109 => ['07081804', '68084774', '25091201'],
1111111111 => ['14050471', '67062674', '99943326'],
1234567890 => ['89005924', '91819424', '93441116'],
2000000000 => ['69279037', '90698825', '38618901'],
20000000000 => ['65353130', '77737706', '47863826']
];

/**
* @var Two_Factor_Totp
*/
protected $provider;

/**
Expand Down Expand Up @@ -192,4 +207,121 @@ public function test_is_valid_authcode() {
$this->assertTrue( $this->provider->is_valid_authcode( $key, $authcode ) );
}

/**
* @covers Two_Factor_Totp::calc_totp
*/
public function test_sha1_generate() {
if ( PHP_INT_SIZE === 4 ) {
$this->markTestSkipped( 'calc_totp requires 64-bit PHP' );
}

$provider = $this->provider;
$hash = 'sha1';
$token = $provider->base32_encode( self::$token );

foreach (self::$vectors as $time => $vector) {
$provider::__set_time( (int) $time );
$this->assertEquals( $vector[0], $provider::calc_totp( $token, false, 8, $hash, self::$step ) );
$this->assertEquals( substr( $vector[0], 2 ), $provider::calc_totp( $token, false, 6, $hash, self::$step ) );
}
}

/**
* @covers Two_Factor_Totp::is_valid_authcode
* @covers Two_Factor_Totp::calc_totp
*/
public function test_sha1_authenticate() {
if ( PHP_INT_SIZE === 4 ) {
$this->markTestSkipped( 'calc_totp requires 64-bit PHP' );
}

$provider = $this->provider;
$hash = 'sha1';
$token = $provider->base32_encode( self::$token );

foreach ( self::$vectors as $time => $vector ) {
$provider::__set_time( (int) $time );
$this->assertTrue( $provider::is_valid_authcode( $token, $vector[0], $hash ) );
$this->assertTrue( $provider::is_valid_authcode( $token, substr( $vector[0], 2 ), $hash ) );
}
}

/**
* @covers Two_Factor_Totp::calc_totp
*/
public function test_sha256_generate() {
if (PHP_INT_SIZE === 4) {
$this->markTestSkipped( 'calc_totp requires 64-bit PHP' );
}

$provider = $this->provider;
$hash = 'sha256';
$token = $provider->base32_encode( self::$token );

foreach ( self::$vectors as $time => $vector ) {
$provider::__set_time( (int) $time );
$this->assertEquals( $vector[1], $provider::calc_totp( $token, false, 8, $hash, self::$step ) );
$this->assertEquals( substr( $vector[1], 2 ), $provider::calc_totp( $token, false, 6, $hash, self::$step ) );
}
}

/**
* @covers Two_Factor_Totp::is_valid_authcode
* @covers Two_Factor_Totp::calc_totp
*/
public function test_sha256_authenticate() {
if ( PHP_INT_SIZE === 4 ) {
$this->markTestSkipped( 'calc_totp requires 64-bit PHP' );
}

$provider = $this->provider;
$hash = 'sha256';
$token = $provider->base32_encode( self::$token );

foreach ( self::$vectors as $time => $vector ) {
$provider::__set_time( (int) $time );
$this->assertTrue( $provider::is_valid_authcode( $token, $vector[1], $hash ) );
$this->assertTrue( $provider::is_valid_authcode( $token, substr( $vector[1], 2 ), $hash ) );
}
}

/**
* @covers Two_Factor_Totp::calc_totp
*/
public function test_sha512_generate() {
if ( PHP_INT_SIZE === 4 ) {
$this->markTestSkipped('calc_totp requires 64-bit PHP');
}

$provider = $this->provider;
$hash = 'sha512';
$token = $provider->base32_encode( self::$token );

foreach ( self::$vectors as $time => $vector ) {
$provider::__set_time( (int) $time );
$this->assertEquals( $vector[2], $provider::calc_totp( $token, false, 8, $hash, self::$step ) );
$this->assertEquals( substr($vector[2], 2 ), $provider::calc_totp( $token, false, 6, $hash, self::$step ) );
}
}

/**
* @covers Two_Factor_Totp::is_valid_authcode
* @covers Two_Factor_Totp::calc_totp
*/
public function test_sha512_authenticate() {
if ( PHP_INT_SIZE === 4 ) {
$this->markTestSkipped( 'calc_totp requires 64-bit PHP' );
}

$provider = $this->provider;
$hash = 'sha512';
$token = $provider->base32_encode( self::$token );

foreach ( self::$vectors as $time => $vector ) {
$provider::__set_time( (int) $time );
$this->assertTrue( $provider::is_valid_authcode( $token, $vector[2], $hash ) );
$this->assertTrue( $provider::is_valid_authcode( $token, substr( $vector[2], 2 ), $hash ) );
}

}
}

0 comments on commit 9c7d057

Please sign in to comment.