-
-
Notifications
You must be signed in to change notification settings - Fork 138
remove insecure rng providers #122
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
d1792f0
e44997c
f6da6be
06220d4
d5a3acc
442eca0
841fd4e
17f713b
ca921e4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,12 +4,12 @@ | |
|
||
namespace RobThree\Auth; | ||
|
||
use function hash_equals; | ||
|
||
use RobThree\Auth\Providers\Qr\IQRCodeProvider; | ||
use RobThree\Auth\Providers\Qr\QRServerProvider; | ||
use RobThree\Auth\Providers\Rng\CSRNGProvider; | ||
use RobThree\Auth\Providers\Rng\HashRNGProvider; | ||
use RobThree\Auth\Providers\Rng\IRNGProvider; | ||
use RobThree\Auth\Providers\Rng\OpenSSLRNGProvider; | ||
use RobThree\Auth\Providers\Time\HttpTimeProvider; | ||
use RobThree\Auth\Providers\Time\ITimeProvider; | ||
use RobThree\Auth\Providers\Time\LocalMachineTimeProvider; | ||
|
@@ -51,14 +51,11 @@ public function __construct( | |
/** | ||
* Create a new secret | ||
*/ | ||
public function createSecret(int $bits = 80, bool $requirecryptosecure = true): string | ||
public function createSecret(int $bits = 80): string | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. mental note to document in the changelog |
||
{ | ||
$secret = ''; | ||
$bytes = (int)ceil($bits / 5); // We use 5 bits of each byte (since we have a 32-character 'alphabet' / BASE32) | ||
$rngprovider = $this->getRngProvider(); | ||
if ($requirecryptosecure && !$rngprovider->isCryptographicallySecure()) { | ||
throw new TwoFactorAuthException('RNG provider is not cryptographically secure'); | ||
} | ||
$rnd = $rngprovider->getRandomBytes($bytes); | ||
for ($i = 0; $i < $bytes; $i++) { | ||
$secret .= self::$_base32[ord($rnd[$i]) & 31]; //Mask out left 3 bits for 0-31 values | ||
|
@@ -98,7 +95,7 @@ public function verifyCode(string $secret, string $code, int $discrepancy = 1, ? | |
for ($i = -$discrepancy; $i <= $discrepancy; $i++) { | ||
$ts = $timestamp + ($i * $this->period); | ||
$slice = $this->getTimeSlice($ts); | ||
$timeslice = $this->codeEquals($this->getCode($secret, $ts), $code) ? $slice : $timeslice; | ||
$timeslice = hash_equals($this->getCode($secret, $ts), $code) ? $slice : $timeslice; | ||
} | ||
|
||
return $timeslice > 0; | ||
|
@@ -174,19 +171,7 @@ public function getQrCodeProvider(): IQRCodeProvider | |
*/ | ||
public function getRngProvider(): IRNGProvider | ||
{ | ||
if ($this->rngprovider !== null) { | ||
return $this->rngprovider; | ||
} | ||
if (function_exists('random_bytes')) { | ||
return $this->rngprovider = new CSRNGProvider(); | ||
} | ||
if (function_exists('openssl_random_pseudo_bytes')) { | ||
return $this->rngprovider = new OpenSSLRNGProvider(); | ||
} | ||
if (function_exists('hash')) { | ||
return $this->rngprovider = new HashRNGProvider(); | ||
} | ||
throw new TwoFactorAuthException('Unable to find a suited RNGProvider'); | ||
return $this->rngprovider ??= new CSRNGProvider(); | ||
} | ||
|
||
public function getTimeProvider(): ITimeProvider | ||
|
@@ -195,27 +180,6 @@ public function getTimeProvider(): ITimeProvider | |
return $this->timeprovider ??= new LocalMachineTimeProvider(); | ||
} | ||
|
||
/** | ||
* Timing-attack safe comparison of 2 codes (see http://blog.ircmaxell.com/2014/11/its-all-about-time.html) | ||
*/ | ||
private function codeEquals(string $safe, string $user): bool | ||
{ | ||
if (function_exists('hash_equals')) { | ||
return hash_equals($safe, $user); | ||
} | ||
// In general, it's not possible to prevent length leaks. So it's OK to leak the length. The important part is that | ||
// we don't leak information about the difference of the two strings. | ||
if (strlen($safe) === strlen($user)) { | ||
$result = 0; | ||
$strlen = strlen($safe); | ||
for ($i = 0; $i < $strlen; $i++) { | ||
$result |= (ord($safe[$i]) ^ ord($user[$i])); | ||
} | ||
return $result === 0; | ||
} | ||
return false; | ||
} | ||
|
||
private function getTime(?int $time = null): int | ||
{ | ||
return $time ?? $this->getTimeProvider()->getTime(); | ||
|
This file was deleted.
This file was deleted.
This file was deleted.
Uh oh!
There was an error while loading. Please reload this page.