KD2 Framework  Security_OTP

Provides support for TOTP (time based) and HOTP (HMAC, counter based) one time password, following RFC 6238. Useful for two factor authentication (2FA), and is compatible with most popular solutions like Google Authenticator.

Recommended client for Android and iOS is FreeOTP: https://freeotp.github.io/

Security_OTP features

  • Generate a secret
  • Base32 decode / encode (RFC 3548)
  • Protected against timing attacks
  • TOTP support
  • HOTP support
  • Supports time drift
  • Supports multiple digests: SHA1 (default), SHA256 or SHA512
  • Supports any number of digits
  • Can be used to generate new codes as well as compare them (client or server scenario)

Usage example: generating a new code

echo \KD2\Security_OTP::TOTP($secret);

Usage example: comparing a code sent by client

$success = \KD2\Security_OTP::TOTP($secret, $_POST['code']);

if ($success)
    echo 'TOTP auth was successful';
else
    echo 'TOTP auth failed';

Usage example: comparing a code using NTP time

This is quite useful if your code is used on servers you don't manage, and where server time is sometimes out of sync and causes trouble with TOTP.

$time = \KD2\Security_OTP::getTimeFromNTP('nz.pool.ntp.org');
$success = \KD2\Security_OTP::TOTP($secret, $_POST['code'], $time);

if ($success)
    echo 'TOTP auth was successful';
else
    echo 'TOTP auth failed';

Usage example: creating a random secret and displaying it with a QR code

$secret = \KD2\Security_OTP::getRandomSecret();
$secret_display = implode(' ', str_split($secret, 4));
$otp_url = \KD2\Security_OTP::getOTPAuthURL('My website', $secret);
$qrcode = (new \KD2\QRCode($otp_url))->toSVG();
$qrcode_encoded = 'data:image/svg+xml;base64,' . base64_encode($qrcode);

echo '<p>Your secret is:</p>';
echo '<pre>' . htmlspecialchars($secret_display) . '</pre>';
echo '<p>Scan this QRCode in FreeOTP to add it:</p>';
echo '<img src="' . htmlspecialchars($qrcode_encoded) . '" />';

API doc

Security_OTP::HOTP($secret, $count, $code = null, $digits = 6, $digest = 'sha1')

Creates or checks a HOTP code

Note that you should store $count yourself, as well as store codes that have already been used successfully, and reject any used code to protect against replay attacks.

@param string $secret Secret key (Base32 encoded)
@param integer $count Counter or timestamp for TOTP
@param integer $code  Code to check against, if null is passed, then a code will be generated
@param integer $digits Number of digits in the generated code (default is 6)
@param string $digest Digest algo to use (sha1, sha256 or sha512) default is sha1

Security_OTP::TOTP($secret, $code = null, $timestamp = time(), $digits = 6, $digest = 'sha1', $interval = 30, $drift = 1)

Time based One-time password (RFC 6238)

Note that you should store codes that have already been used successfully, and reject any previously used code to protect against replay attacks.

@param string $secret    Secret key
@param integer $code     One-time code to check against, if NULL a new code will be returned
@param integer $timestamp UNIX timestamp (in seconds) or NULL to use the system time
@param integer $digits 	Number of digits in the generated code (default is 6)
@param string $digest 	Digest algo to use (sha1, sha256 or sha512) default is sha1
@param integer $interval Time interval to round the timestamp (default is 30 seconds)
@param integer $drift    Number of intervals in the past and future to try
This is useful for clients who use an invalid time to generate the OTP.

Security_OTP::getRandomSecret($length = 16)

Returns a random base32 secret for HOTP/TOTP

@param integer $length Length of the secret key (default is 16 characters)

Security_OTP::getOTPAuthURL($label, $secret, $type = 'totp')

Returns a valid otpauth:// URL from a secret (Useful to generate QRcodes)

Details: https://github.com/google/google-authenticator/wiki/Key-Uri-Format

@param  string $label Service label, eg 'Blog:james@alice.com'
@param  string $secret secret key
@param  string $type 'totp' or 'hotp'

Security_OTP::getTimeFromNTP($host = 'pool.ntp.org', $timeout = 10)

Returns current time as an UNIX timestamp from a NTP server (atomic time).

@param  string  $host    Server host (default is pool.ntp.org)
@param  integer $timeout Timeout  in seconds (default is 10 seconds)
@return integer Number of seconds since January 1st 1970