Skip to content
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

Deprecate ::create API, add ::createFromSecret and ::generate APIs #166

Merged
merged 6 commits into from
Nov 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 9 additions & 11 deletions doc/AppConfig.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ You just have to:
<?php
use OTPHP\TOTP;

$totp = TOTP::create('JBSWY3DPEHPK3PXP'); // New TOTP with custom secret
$totp = TOTP::createFromSecret('JBSWY3DPEHPK3PXP'); // New TOTP with custom secret
$totp->setLabel('alice@google.com'); // The label (string)

$totp->getProvisioningUri(); // Will return otpauth://totp/alice%40google.com?secret=JBSWY3DPEHPK3PXP
Expand All @@ -34,7 +34,7 @@ Hereafter two examples using the Google Chart API (this API is deprecated since
<?php
use OTPHP\TOTP;

$totp = TOTP::create(); // New TOTP
$totp = TOTP::generate(); // New TOTP
$totp->setLabel('alice@google.com'); // The label (string)

$google_chart = $totp->getQrCodeUri('https://chart.googleapis.com/chart?chs=200x200&chld=M|0&cht=qr&chl={PROVISIONING_URI}', '{PROVISIONING_URI}');
Expand All @@ -48,7 +48,7 @@ Please note that this URI MUST contain a placeholder for the OTP Provisioning UR
<?php
use OTPHP\TOTP;

$totp = TOTP::create(); // New TOTP
$totp = TOTP::generate(); // New TOTP
$totp->setLabel('alice@google.com'); // The label (string)

$goqr_me = $totp->getQrCodeUri(
Expand All @@ -74,7 +74,7 @@ Now run the following and compare the output
<?php
use OTPHP\TOTP;

$totp = TOTP::create('JBSWY3DPEHPK3PXP'); // New TOTP with custom secret
$totp = TOTP::createFromSecret('JBSWY3DPEHPK3PXP'); // New TOTP with custom secret
$totp->setLabel('alice@google.com'); // The label (string)

echo 'Current OTP: ' . $totp->now();
Expand All @@ -92,13 +92,11 @@ Now run the following and compare the output
<?php
use OTPHP\TOTP;

$totp = TOTP::create(
'JBSWY3DPEHPK3PXP', // New TOTP with custom secret
10, // The period (int)
'sha512', // The digest algorithm (string)
8 // The number of digits (int)
);
$totp->setLabel('alice@google.com'); // The label (string)
$totp = TOTP::createFromSecret('JBSWY3DPEHPK3PXP'); // New TOTP with custom secret
$totp->setPeriod(10); // The period (int)
$totp->setDigest('sha512'); // The digest algorithm (string)
$totp->setDigits(8); // The number of digits (int)
$totp->setLabel('alice@google.com'); // The label (string)

echo 'Current OTP: ' . $totp->now();
```
60 changes: 24 additions & 36 deletions doc/Customize.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use OTPHP\TOTP;
use ParagonIE\ConstantTime\Base32;

$mySecret = trim(Base32::encodeUpper(random_bytes(128)), '='); // We generate our own 1024 bits secret
$otp = TOTP::create($mySecret);
$otp = TOTP::createFromSecret($mySecret);
```

*Please note that the trailing `=` are automatically removed by the library.*
Expand All @@ -35,15 +35,11 @@ By default, the period for a TOTP is 30 seconds and the counter for a HOTP is 0.
use OTPHP\TOTP;
use OTPHP\HOTP;

$otp = TOTP::create(
null, // Let the secret be defined by the class
10 // The period is now 10 seconds
);
$otp = TOTP::generate();
$otp->setPeriod(10); // The period is now 10 seconds

$otp = HOTP::create(
null, // Let the secret be defined by the class
1000 // The counter is now 1000. We recommend you start at `0`, but you can set any value (at least 0)
);
$otp = HOTP::generate();
$otp->setCounter(1000); // The counter is now 1000. We recommend you start at `0`, but you can set any value (at least 0)
```

## Digest
Expand All @@ -59,11 +55,9 @@ You must verify that the algorithm you want to use is supported by the applicati
<?php
use OTPHP\TOTP;

$totp = TOTP::create(
null, // Let the secret be defined by the class
30, // The period (30 seconds)
'ripemd160' // The digest algorithm
);
$totp = TOTP::generate();
$totp->setPeriod(30); // The period (30 seconds)
$totp->setDigest('ripemd160'); // The digest algorithm
```

## Digits
Expand All @@ -75,12 +69,10 @@ You can decide to use more (or less) digits. More than 10 may be difficult to us
<?php
use OTPHP\TOTP;

$totp = TOTP::create(
null, // Let the secret be defined by the class
30, // The period (30 seconds)
'sha1', // The digest algorithm
8 // The output will generate 8 digits
);
$totp = TOTP::generate();
$totp->setPeriod(30); // The period (30 seconds)
$totp->setDigest('sha1'); // The digest algorithm
$totp->setDigits(8); // The output will generate 8 digits
```

## Epoch (TOTP only)
Expand All @@ -100,26 +92,22 @@ example encode the timestamp in the secret to make it different each time.
use OTPHP\TOTP;

// Without epoch
$otp = TOTP::create(
null, // Let the secret be defined by the class
5, // The period (5 seconds)
'sha1', // The digest algorithm
6 // The output will generate 6 digits
);
$otp = TOTP::generate();
$otp->setPeriod(5); // The period (5 seconds)
$otp->setDigest('sha1'); // The digest algorithm
$otp->setDigits(6); // The output will generate 6 digits

$password = $otp->at(1519401289); // Current period is: 1519401285 - 1519401289

$otp->verify($password, 1519401289); // Second 1: true
$otp->verify($password, 1519401290); // Second 2: false

// With epoch
$otp = TOTP::create(
null, // Let the secret be defined by the class
5, // The period (5 seconds)
'sha1', // The digest algorithm
6, // The output will generate 6 digits
1519401289 // The epoch is now 02/23/2018 @ 3:54:49pm (UTC)
);
$otp = TOTP::generate();
$otp->setPeriod(5); // The period (30 seconds)
$otp->setDigest('sha1'); // The digest algorithm
$otp->setDigits(6); // The output will generate 8 digits
$otp->setEpoch(1519401289); // The epoch is now 02/23/2018 @ 3:54:49pm (UTC)

$password = $otp->at(1519401289); // Current period is: 1519401289 - 1519401293

Expand All @@ -140,7 +128,7 @@ These parameters are available in the provisioning URI or from the method `getPa
<?php
use OTPHP\TOTP;

$totp = TOTP::create('JBSWY3DPEHPK3PXP'); // New TOTP
$totp = TOTP::createFromSecret('JBSWY3DPEHPK3PXP'); // New TOTP
$totp->setLabel('alice@google.com'); // The label
$totp->setParameter('foo', 'bar');

Expand All @@ -156,7 +144,7 @@ it is useful to set the issuer parameter to identify the service that provided t
<?php
use OTPHP\TOTP;

$totp = TOTP::create('JBSWY3DPEHPK3PXP'); // New TOTP with custom secret
$totp = TOTP::createFromSecret('JBSWY3DPEHPK3PXP'); // New TOTP with custom secret
$totp->setLabel('alice@google.com'); // The label (string)
$totp->setIssuer('My Service');
```
Expand Down Expand Up @@ -187,7 +175,7 @@ Some applications such as FreeOTP can load images from an URI (`image` parameter
<?php
use OTPHP\TOTP;

$totp = TOTP::create('JBSWY3DPEHPK3PXP'); // New TOTP with custom secret
$totp = TOTP::createFromSecret('JBSWY3DPEHPK3PXP'); // New TOTP with custom secret
$totp->setLabel('alice@google.com'); // The label (string)
$totp->setParameter('image', 'https://foo.bar/otp.png');

Expand Down
12 changes: 5 additions & 7 deletions doc/QA.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,10 @@ $digits = 6;
$digest = 'sha1';
$period = 30;

$totp = TOTP::create(
$user->getOtpSecret(),
$period,
$digest,
$digits
);
$totp = TOTP::createFromSecret($user->getOtpSecret());
$totp->setPeriod($period);
$totp->setDigest($digest);
$totp->setDigits($digits);
$totp->setLabel($user->getEmail());

$totp->verify($_POST['otp']);
Expand Down Expand Up @@ -76,7 +74,7 @@ If you try the following code lines, you may see 2 different OTPs.
<?php
use OTPHP\TOTP;

$totp = TOTP::create(null, 10); // TOTP with an 10 seconds period
$totp = TOTP::generate(10); // TOTP with an 10 seconds period

for ($i = 0; $i < 10; $i++) {
echo 'Current OTP is: '. $totp->now();
Expand Down
6 changes: 3 additions & 3 deletions doc/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,13 @@ use OTPHP\TOTP;

// A random secret will be generated from this.
// You should store the secret with the user for verification.
$otp = TOTP::create();
$otp = TOTP::generate();
echo "The OTP secret is: {$otp->getSecret()}\n";

// Note: use your own way to load the user secret.
// The function "load_user_secret" is simply a placeholder.
$secret = load_user_secret();
$otp = TOTP::create($secret);
$otp = TOTP::createFromSecret($secret);
echo "The current OTP is: {$otp->now()}\n";
```

Expand All @@ -74,7 +74,7 @@ echo "<img src='{$grCodeUri}'>";
Now that your applications are configured, you can verify the generated OTPs:

```php
$otp = TOTP::create($secret); // create TOTP object from the secret.
$otp = TOTP::createFromSecret($secret); // create TOTP object from the secret.
$otp->verify($input); // Returns true if the input is verified, otherwise false.
```

Expand Down
1 change: 1 addition & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ parameters:
- tests
ignoreErrors:
- '#Variable property access on \$this\(OTPHP\\OTP\)\.#'
- '#^Method OTPHP\\OTP::generateSecret\(\) should return non-empty-string but returns string\.$#'

includes:
- vendor/phpstan/phpstan-strict-rules/rules.neon
Expand Down
4 changes: 2 additions & 2 deletions src/Factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,12 @@ private static function createOTP(Url $parsed_url): OTPInterface
{
switch ($parsed_url->getHost()) {
case 'totp':
$totp = TOTP::create($parsed_url->getSecret());
$totp = TOTP::createFromSecret($parsed_url->getSecret());
$totp->setLabel(self::getLabel($parsed_url->getPath()));

return $totp;
case 'hotp':
$hotp = HOTP::create($parsed_url->getSecret());
$hotp = HOTP::createFromSecret($parsed_url->getSecret());
$hotp->setLabel(self::getLabel($parsed_url->getPath()));

return $hotp;
Expand Down
39 changes: 28 additions & 11 deletions src/HOTP.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,36 @@
*/
final class HOTP extends OTP implements HOTPInterface
{
protected function __construct(null|string $secret, int $counter, string $digest, int $digits)
{
parent::__construct($secret, $digest, $digits);
$this->setCounter($counter);
}

public static function create(
null|string $secret = null,
int $counter = 0,
string $digest = 'sha1',
int $digits = 6
int $counter = self::DEFAULT_COUNTER,
string $digest = self::DEFAULT_DIGEST,
int $digits = self::DEFAULT_DIGITS
): self {
return new self($secret, $counter, $digest, $digits);
$htop = $secret !== null
? self::createFromSecret($secret)
: self::generate()
;
$htop->setCounter($counter);
$htop->setDigest($digest);
$htop->setDigits($digits);

return $htop;
}

public static function createFromSecret(string $secret): self
{
$htop = new self($secret);
$htop->setCounter(self::DEFAULT_COUNTER);
$htop->setDigest(self::DEFAULT_DIGEST);
$htop->setDigits(self::DEFAULT_DIGITS);

return $htop;
}

public static function generate(): self
{
return self::createFromSecret(self::generateSecret());
}

public function getCounter(): int
Expand Down Expand Up @@ -58,7 +75,7 @@ public function verify(string $otp, null|int $counter = null, null|int $window =
return $this->verifyOtpWithWindow($otp, $counter, $window);
}

protected function setCounter(int $counter): void
public function setCounter(int $counter): void
{
$this->setParameter('counter', $counter);
}
Expand Down
11 changes: 10 additions & 1 deletion src/HOTPInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,29 @@

interface HOTPInterface extends OTPInterface
{
public const DEFAULT_COUNTER = 0;

/**
* The initial counter (a positive integer).
*/
public function getCounter(): int;

/**
* Create a new TOTP object.
* Create a new HOTP object.
*
* If the secret is null, a random 64 bytes secret will be generated.
*
* @param null|non-empty-string $secret
* @param non-empty-string $digest
*
* @deprecated Deprecated since v11.1, use ::createFromSecret or ::generate instead
*/
public static function create(
null|string $secret = null,
int $counter = 0,
string $digest = 'sha1',
int $digits = 6
): self;

public function setCounter(int $counter): void;
}
15 changes: 12 additions & 3 deletions src/OTP.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@ abstract class OTP implements OTPInterface
{
use ParameterTrait;

protected function __construct(null|string $secret, string $digest, int $digits)
/**
* @param non-empty-string $secret
*/
protected function __construct(string $secret)
{
$this->setSecret($secret);
$this->setDigest($digest);
$this->setDigits($digits);
}

public function getQrCodeUri(string $uri, string $placeholder): string
Expand All @@ -36,6 +37,14 @@ public function at(int $input): string
return $this->generateOTP($input);
}

/**
* @return non-empty-string
*/
final protected static function generateSecret(): string
{
return Base32::encodeUpper(random_bytes(64));
}

/**
* The OTP at the specified input.
*/
Expand Down
Loading