Skip to content

Commit

Permalink
Hashids salt doesn't accept a binary string.
Browse files Browse the repository at this point in the history
Convert hash to alphanumeric (base 36) string.
Removed oldCalcChecksum BC for HashidsConfirmation.
Use hmac to create hashids salt.
Fixes #16

**BC break!**
  • Loading branch information
jasny committed Jan 31, 2021
1 parent f6f8cad commit 321670a
Show file tree
Hide file tree
Showing 2 changed files with 7 additions and 37 deletions.
28 changes: 4 additions & 24 deletions src/Confirmation/HashidsConfirmation.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ class HashidsConfirmation implements ConfirmationInterface
/**
* HashidsConfirmation constructor.
*
* @phpstan-param string $secret
* @phpstan-param null|callable(string):Hashids $createHashids
* @param string $secret
* @param null|callable(string):Hashids $createHashids
*/
public function __construct(string $secret, ?callable $createHashids = null)
{
Expand Down Expand Up @@ -246,9 +246,8 @@ protected function fetchUserFromStorage(string $uid, array $context): User
protected function verifyChecksum(string $checksum, User $user, CarbonImmutable $expire, array $context): void
{
$expected = $this->calcChecksum($user, $expire);
$expectedOld = $this->calcOldChecksum($user, $expire);

if ($checksum === $expected || $checksum === $expectedOld) {
if ($checksum === $expected) {
return;
}

Expand Down Expand Up @@ -288,31 +287,12 @@ protected function calcChecksum(User $user, \DateTimeInterface $expire): string
return hash_hmac('sha256', join("\0", $parts), $this->secret);
}

/**
* Calculate confirmation checksum, before switching to hmac.
* Temporary so existing confirmation tokens will continue working. Will be removed.
*
* @deprecated
*/
protected function calcOldChecksum(User $user, \DateTimeInterface $expire): string
{
$parts = [
CarbonImmutable::instance($expire)->utc()->format('YmdHis'),
$user->getAuthId(),
$user->getAuthChecksum(),
$this->secret,
];

return hash('sha256', join("\0", $parts));
}


/**
* Create a hashids service.
*/
public function createHashids(): Hashids
{
$salt = hash('sha256', $this->subject . $this->secret, true);
$salt = base_convert(hash_hmac('sha256', $this->subject, $this->secret), 16, 36);

return ($this->createHashids)($salt);
}
Expand Down
16 changes: 3 additions & 13 deletions tests/Confirmation/HashidsConfirmationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class HashidsConfirmationTest extends TestCase
use ExpectWarningTrait;
use CallbackMockTrait;

protected const TOKEN = 'kR2wngZKmZsKRN6xWKy7U1qEWBnWxaf60YjPDakjcXKB1v2rOZt831bDOGk6hJ6WBgGBm';
protected const TOKEN = 'o1rLAl8m28S8v34QK8V7So8nrKOrBVFjdZgOkzmguBMNzQvyjXIn6ma1Ro9Bh8L7Mk1M0';
protected const STD_HEX = '43b87e6e92e84566b79f6f16ee4c982accec20d16bc3e46c8656bcef93dafba6202001011200003432';
protected const OLD_HEX = '8930d6fab596adc131412a8309d5391611047dcf9dad6e106ccbb5b8ee2ae7fb202001011200003432';

Expand Down Expand Up @@ -152,16 +152,6 @@ public function testFrom()
$this->assertSame($this->user, $confirm->from(self::TOKEN));
}

public function testFromWithOldToken()
{
$confirm = $this->createService(self::OLD_HEX, $this->user);

$this->logger->expects($this->once())->method('info')
->with('Verified confirmation token', $this->expectedContext('42', '2020-01-01T12:00:00+00:00'));

$this->assertSame($this->user, $confirm->from(self::TOKEN));
}

public function testFromWithCustomUidEncoding()
{
$hex = substr(self::STD_HEX, 0, -4) . '2a';
Expand Down Expand Up @@ -254,7 +244,7 @@ public function testFromTokenWithInvalidExpireDate()

public function testFromExpiredToken()
{
$hex = 'b087edc903ba55d052e51aa2f8a01bc8e68c9503778eedc941e9932b36dd8d09' . '20191101120000' . '3432';
$hex = '3e912b083116f9063a7e0f6fb67179c024d4419aba564f4d898b0c033dc3285b' . '20191101120000' . '3432';

$confirm = $this->createService($hex, $this->user);

Expand All @@ -274,7 +264,7 @@ public function testCreateHashIdsWithCallback()
/** @var Hashids&MockObject $hashids */
$hashids = $this->createMock(Hashids::class);

$salt = hash('sha256', 'testsecret', true);
$salt = base_convert(hash_hmac('sha256', 'test', 'secret'), 16, 36);
$callback = $this->createCallbackMock($this->once(), [$salt], $hashids);

$service = (new HashidsConfirmation('secret', $callback))
Expand Down

0 comments on commit 321670a

Please sign in to comment.