Skip to content

Latest commit

 

History

History
253 lines (184 loc) · 8.18 KB

File metadata and controls

253 lines (184 loc) · 8.18 KB

OTP Customization

To generate one-time passwords, each class needs at least the following parameters:

  • A secret encoded in Base32
  • A digest algorithm
  • A number of digits

Depending on the type of OTP, you will need the following additional parameters:

  • For TOTP: a period (and optionally an epoch)
  • For HOTP: a counter

Mutable vs Immutable API

Since v11.4, OTPHP provides two ways to modify OTP objects:

Mutable Methods (using set*)

These methods modify the OTP object in place and are useful for building OTP configurations step by step:

<?php
use OTPHP\TOTP;
use OTPHP\InternalClock;

$otp = TOTP::generate(new InternalClock());
$otp->setPeriod(60);           // Modifies the object
$otp->setDigest('sha256');     // Modifies the object
$otp->setDigits(8);            // Modifies the object
$otp->setLabel('alice@foo.bar'); // Modifies the object
$otp->setIssuer('My Service'); // Modifies the object
// $otp is now configured with all the above settings

Immutable Methods (using with*)

These methods return a new OTP object with the modified parameter, leaving the original unchanged. This is useful for readonly classes and functional programming styles:

<?php
use OTPHP\TOTP;
use OTPHP\InternalClock;

$baseOtp = TOTP::generate(new InternalClock());

$customOtp = $baseOtp
    ->withPeriod(60)
    ->withDigest('sha256')
    ->withDigits(8)
    ->withLabel('alice@foo.bar')
    ->withIssuer('My Service');

// $baseOtp remains unchanged with default settings
// $customOtp is a new object with the custom settings

Available immutable methods:

  • withSecret(string $secret): self
  • withLabel(string $label): self
  • withIssuer(string $issuer): self
  • withDigits(int $digits): self
  • withDigest(string $digest): self
  • withParameter(string $parameter, mixed $value): self
  • withIssuerIncludedAsParameter(bool $issuerIncludedAsParameter): self
  • TOTP specific: withPeriod(int $period): self, withEpoch(int $epoch): self
  • HOTP specific: withCounter(int $counter): self

Secret

By default, a 512 bits secret is generated. If you need, you can use your own secret:

<?php
use OTPHP\TOTP;
use ParagonIE\ConstantTime\Base32;

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

You can also specify a custom secret size when generating a new OTP:

<?php
use OTPHP\TOTP;
use OTPHP\InternalClock;

// Generate with custom secret size (in bytes)
// 16 bytes = 26 base32 characters, 32 bytes = 52 base32 characters, etc.
$otp = TOTP::generate(new InternalClock(), 16);
echo "Secret: " . $otp->getSecret(); // Will be 26 characters long

Please note that the trailing = are automatically removed by the library.

Period and Counter

By default, the period for a TOTP is 30 seconds and the counter for a HOTP is 0.

<?php
use OTPHP\TOTP;
use OTPHP\HOTP;

$otp = TOTP::generate();
$otp = $otp->withPeriod(10);    // The period is now 10 seconds

$otp = HOTP::generate();
$otp = $otp->withCounter(1000); // The counter is now 1000. We recommend you start at `0`, but you can set any value (at least 0)

Digest

By default the digest algorithm is sha1. You can use any algorithm listed by hash_algos(). Note that most applications only support md5, sha1, sha256 and sha512. You must verify that the algorithm you want to use is supported by the application your clients might be using.

SHA-2 algorithms are recommended.

<?php
use OTPHP\TOTP;

$totp = TOTP::generate();
$totp = $totp->withPeriod(30)           // The period (30 seconds)
    ->withDigest('ripemd160');  // The digest algorithm

Digits

By default the number of digits is 6. You can decide to use more (or less) digits. More than 10 may be difficult to use by the owner.

<?php
use OTPHP\TOTP;

$totp = TOTP::generate();
$totp = $totp->withPeriod(30)       // The period (30 seconds)
    ->withDigest('sha1')   // The digest algorithm
    ->withDigits(8);        // The output will generate 8 digits

Epoch (TOTP only)

By default, the epoch for a TOTP is 0. The epoch is equivalent to the T0 parameter in RFC 6238. This parameter basically determines at which timestamp (epoch) to start counting. It is useful in scenarios where you need an exact period to verify passwords in. The epoch can be shared by client and server to specify the exact timestamp at which the password was created so that you can reuse it for exact verification.

CAUTION: If you follow this approach and share the epoch as password creation timestamp, you should use dynamic secrets that are different each time, otherwise you will most likely always produce the same passwords. You could for example encode the timestamp in the secret to make it different each time.

<?php
use OTPHP\TOTP;

// Without epoch
$otp = TOTP::generate();
$otp = $otp->withPeriod(5)         // The period (5 seconds)
    ->withDigest('sha1')    // The digest algorithm
    ->withDigits(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::generate();
$otp = $otp->withPeriod(5)         // The period (30 seconds)
    ->withDigest('sha1')    // The digest algorithm
    ->withDigits(6)         // The output will generate 8 digits
    ->withEpoch(1519401289); // The epoch is now 02/23/2018 @ 3:54:49pm (UTC)

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

$otp->verify($password, 1519401289); // Second 1: true
$otp->verify($password, 1519401290); // Second 2: true
$otp->verify($password, 1519401291); // Second 3: true
$otp->verify($password, 1519401292); // Second 4: true
$otp->verify($password, 1519401293); // Second 5: true
$otp->verify($password, 1519401294); // Second 6: false

Custom parameters

OTP objects are able to support custom parameters. These parameters are available in the provisioning URI or from the method getParameter.

<?php
use OTPHP\TOTP;

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

$totp->getProvisioningUri(); // Will return otpauth://totp/alice%40google.com?secret=JBSWY3DPEHPK3PXP&foo=bar

Issuer

As a user may have multiple OTP using the same label (e.g. the user email), it is useful to set the issuer parameter to identify the service that provided the OTP.

<?php
use OTPHP\TOTP;

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

By default and to be compatible with Google Authenticator, the issuer is set in the query parameters and as the label prefix.

<?php
echo $totp->getProvisioningUri(); // Will return otpauth://totp/My%20Service%3Aalice%40google.com?issuer=My%20Service&secret=JBSWY3DPEHPK3PXP

If you do not want to get the issuer as a query parameter, you can remove it by using the method withIssuerIncludedAsParameter(bool).

<?php
$totp = $totp->withIssuerIncludedAsParameter(false);
echo $totp->getProvisioningUri(); // Will return otpauth://totp/My%20Service%3Aalice%40google.com?secret=JBSWY3DPEHPK3PXP

Image

Some applications such as FreeOTP can load images from an URI (image parameter).

Please note that at the moment, we cannot list applications that support this parameter.

<?php
use OTPHP\TOTP;

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

$totp->getProvisioningUri(); // Will return otpauth://totp/alice%40google.com?secret=JBSWY3DPEHPK3PXP&image=https%3A%2F%2Ffoo.bar%2Fotp.png

When you load a QR Code using this input data, a compatible application will try to load the image at https://foo.bar/otp.png.