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 Dec 3, 2024
1 parent 3d40c24 commit 93438be
Show file tree
Hide file tree
Showing 2 changed files with 157 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 @@ -51,6 +51,31 @@ protected function __construct() {
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;
}

/**
* Register the rest-api endpoints required for this provider.
*
Expand Down Expand Up @@ -566,7 +591,7 @@ public static function get_authcode_valid_ticktime( $key, $authcode, $hash = sel
$ticks = range( - $max_ticks, $max_ticks );
usort( $ticks, array( __CLASS__, 'abssort' ) );

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

$digits = strlen( $authcode );

Expand Down Expand Up @@ -680,7 +705,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
130 changes: 130 additions & 0 deletions tests/providers/class-two-factor-totp.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,18 @@
*/
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']
];

/**
* Instance of our provider class.
*
Expand Down Expand Up @@ -317,4 +329,122 @@ function test_get_authcode_valid_ticktime() {

$this->assertFalse( Two_Factor_Totp::get_authcode_valid_ticktime( $key, '000000' ) );
}

/**
* @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 93438be

Please sign in to comment.