====== Validate mOTP Token ====== To validate a [[http://motp.sf.net/|Mobile OTP]] Token, you can use this class. It keeps a list of used tokens in a serialized file so that a token can only be used once. * @todo Implement user(=$initsecret)-lockout after 8 failed attempts */ class mOTP { const CHECKFILE = 'motp.used.dat'; const GRACEPERIOD = 3; // ±3 minutes const LOCKPERIOD = 10; // lock used token for 10 minutes protected $offset = 0; // time offset in 10s of seconds for inexact token generators protected $used = array(); // holds used tokens function __construct( $offset = 0 ) { if ( file_exists( self::CHECKFILE ) ) $this->used = unserialize( file_get_contents( self::CHECKFILE ) ); } function __destruct() { if ( is_writable('.') ) file_put_contents( self::CHECKFILE, serialize( $this->used ) ); } function checkOTP( $pin, $otp, $initsecret ) { $time = time(); // old: gmdate('U'); if ( isset( $this->used[$otp] ) && $this->used[$otp]>=$time ) return false; // has been used before else unset( $this->used[$otp] ); // cleanup $otime = floor($time / 10) + $this->offset; $grace = self::GRACEPERIOD * 6; // grace period in 10s of seconds (default: ±18) for ($i=$otime-$grace; $i<=$otime+$grace; $i++) { $md5 = substr( md5( $i . $initsecret . $pin ), 0, 6); if ($otp == $md5) { $this->used[$otp] = $time + self::LOCKPERIOD*60; return true; } } return false; } } ?> ===== Exact time ===== If you use this class on a server with a bad clock, you can replace the line: $time = time(); // old: gmdate('U'); to use the [[query-time-server|NTP_TIME]] class this way: $time = NTP_TIME::query(); if ( $time === false ) $time = time(); ===== See Also ===== * [[:software:php:dw-motp-auth]] * [[:software:python:motp-token-generator]]