Skip to content
This repository has been archived by the owner on Mar 27, 2019. It is now read-only.

Commit

Permalink
Implemented OTP app test suite.
Browse files Browse the repository at this point in the history
  • Loading branch information
ezzatron committed Aug 16, 2013
1 parent fa0627c commit 9b52f4f
Show file tree
Hide file tree
Showing 5 changed files with 384 additions and 20 deletions.
208 changes: 207 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,230 @@
*Otis* is a PHP library for implementing [one-time password] / [two-factor
authentication] systems. *Otis* provides generators and validators for both
[TOTP][] (time-based passwords as defined in [RFC 6238]) and [HOTP][]
(counter-based passwords as covered in [RFC 4226]).
(counter-based passwords as covered in [RFC 4226]). *Otis* supports all hashing
algorithms (SHA-1, SHA-256, SHA-512).

In addition, *Otis* provides tools for generating the URI format understood by
[Google Authenticator] and other compatible OTP apps, as well as URIs for QR
code generation services to further ease integration.

## Usage

### Validating a TOTP password

```php
use Eloquent\Otis\Totp\TotpValidator;

$validator = new TotpValidator;

$password = '<OTP password>'; // the password to validate
$secret = '<OTP secret>'; // the shared secret

$result = $validator->validate($password, $secret);
```

### Validating an HOTP password

```php
use Eloquent\Otis\Hotp\HotpValidator;

$validator = new HotpValidator;

$password = '<OTP password>'; // the password to validate
$secret = '<OTP secret>'; // the shared secret
$counter = 0; // current counter value

$result = $validator->validate($password, $secret, $counter, $newCounter);
if ($result) {
$counter = $newCounter;
}
```

### Generating a Google Authenticator URI

```php
use Eloquent\Otis\GoogleAuthenticator\GoogleAuthenticatorUriFactory;

$uriFactory = new GoogleAuthenticatorUriFactory;

$uri = $uriFactory->createTotpUri('12345678901234567890', 'test.ease@example.org');
echo $uri; // outputs 'otpauth://totp/test.ease%40example.org?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ'
```

### Generating a Google Authenticator QR code URI

```php
use Eloquent\Otis\GoogleAuthenticator\GoogleAuthenticatorUriFactory;
use Eloquent\Otis\QrCode\GoogleChartsQrCodeUriFactory;

$uriFactory = new GoogleAuthenticatorUriFactory;
$qrCodeUriFactory = new GoogleChartsQrCodeUriFactory;

$qrCodeUri = $qrCodeUriFactory->createUri(
$uriFactory->createTotpUri('12345678901234567890', 'test.ease@example.org')
);
echo $qrCodeUri; // outputs 'https://chart.googleapis.com/chart?cht=qr&chs=250x250&chld=%7C0&chl=otpauth%3A%2F%2Ftotp%2Ftest.ease%2540example.org%3Fsecret%3DGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ'
```

### Validating a sequence of HOTP passwords

```php
use Eloquent\Otis\Hotp\HotpValidator;

$validator = new HotpValidator;

// the password sequence to validate
$passwords = array('<OTP password 1>', '<OTP password 2>', '<OTP password 3>');
$secret = '<OTP secret>'; // the shared secret
$counter = 0; // current counter value

$result = $validator->validateSequence($passwords, $secret, $counter, $newCounter);
if ($result) {
$counter = $newCounter;
}
```

## Security considerations

When implementing an OTP system, the following points should be considered with
care:

- Each password should only be considered valid once. This helps to avoid replay
attacks. This is especially important for time-based passwords that may
otherwise be considered valid for an entire time period. Keeping track of
which one-time passwords have already been used in a successful validation is
the only way to ensure a password is not re-used.
- The shared secret should be treated as sensitive information. When storing the
secret on the server side, strong two-way encryption should be used.
- In order for time-based OTP systems to work well, there should be minimal
differences in the system time of the server, and the OTP device in use.
*Otis* defaults allow -1 to +1 time windows (a window is usually 30 seconds),
but the validator can be configured to accept passwords from larger time
windows.

## Try *Otis*

*Otis* has a simple demonstration system. In order to run the demos, these
instructions must be followed:

- [Install Google Authenticator] or a compatible OTP app
- Clone the *Otis* repository
- Install [Composer] dependencies, including dev dependencies
- Run `test/bin/totp` or `test/bin/hotp` depending on which type of OTP system
is preferred
- A link to a QR code image will be launched in the default browser
- Scan this QR code with the OTP app
- Return to the console and enter the passwords provided by the OTP app

In addition, there is a test suite for determining the capabilities of OTP apps.
In order to run the test suite follow these steps:

- Install [Composer] dependencies as above
- Change directory into `test/etc/otp-test-suite`
- Run `php -S localhost:8000`
- Visit http://localhost:8000/ in a browser

## OTP app capabilities

Not all OTP apps support the same features. Even [Google Authenticator] does not
support all the features that its [URI format] is capable of expressing (and
support varies across platforms).

This table is an attempt to cover some of the most popular available OTP apps,
and their features, and is accurate at the time of writing (2013-08-16).

To contribute more data to this table, use the OTP test suite as described in
the [Try Otis][#try-otis] section.

<table>
<thead>
<tr>
<th>Name</th>
<th>Platform</th>
<th>Version</th>
<th>QR</th>
<th>TOTP</th>
<th>HOTP</th>
<th>6-digit</th>
<th>8-digit</th>
<th>10-digit</th>
<th>SHA-1</th>
<th>SHA-256</th>
<th>SHA-512</th>
<th>Non-standard window</th>
<th>Issuer</th>
<th>Legacy issuer</th>
</tr>
</thead>
<tbody>
<tr>
<td>Google Authenticator</td>
<td>Android</td>
<td>2.49</td>
<td>✓</td>
<td>✓</td>
<td>✓</td>
<td>✓</td>
<td>✗</td>
<td>✗</td>
<td>✓</td>
<td>✗</td>
<td>✗</td>
<td>✗</td>
<td>✓</td>
<td>✓</td>
</tr>
<tr>
<td>Google Authenticator</td>
<td>iOS</td>
<td>1.1.4.755</td>
<td>✓</td>
<td>✓</td>
<td>✗</td>
<td>✓</td>
<td>✓</td>
<td>✗</td>
<td>✓</td>
<td>✓</td>
<td>✓</td>
<td>✓</td>
<td>✗</td>
<td>✗</td>
</tr>
<tr>
<td>Google Authenticator</td>
<td>BlackBerry</td>
<td>1.1.1</td>
<td>✗</td>
<td>✓</td>
<td>✓</td>
<td>✓</td>
<td>✗</td>
<td>✗</td>
<td>✓</td>
<td>✗</td>
<td>✗</td>
<td>✗</td>
<td>✗</td>
<td>✗</td>
</tr>
</tbody>
</table>

<!-- References -->

[API documentation]: http://lqnt.co/otis/artifacts/documentation/api/
[Composer]: http://getcomposer.org/
[eloquent/otis]: https://packagist.org/packages/eloquent/otis
[Google Authenticator]: http://en.wikipedia.org/wiki/Google_Authenticator
[HOTP]: http://en.wikipedia.org/wiki/HMAC-based_One-time_Password_Algorithm
[Install Google Authenticator]: https://support.google.com/accounts/answer/1066447?hl=en
[one-time password]: http://en.wikipedia.org/wiki/One-time_password
[RFC 4226]: http://tools.ietf.org/html/rfc4226
[RFC 6238]: http://tools.ietf.org/html/rfc6238
[TOTP]: http://en.wikipedia.org/wiki/Time-based_One-time_Password_Algorithm
[two-factor authentication]: http://en.wikipedia.org/wiki/Multi-factor_authentication
[URI format]: https://code.google.com/p/google-authenticator/wiki/KeyUriFormat

[Build Status]: https://api.travis-ci.org/eloquent/otis.png?branch=master
[Latest build]: https://travis-ci.org/eloquent/otis
Expand Down
20 changes: 9 additions & 11 deletions src/Eloquent/Otis/Hotp/HotpValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,17 +46,17 @@ public function generator()
* @param string $password The password to validate.
* @param string $secret The HOTP secret.
* @param integer $currentCounter The current counter value.
* @param integer|null $window The amount of counter increments to search through for a match.
* @param integer|null &$newCounter Will be set to the new counter value.
* @param integer|null $window The amount of counter increments to search through for a match.
*
* @return boolean True if the password is valid.
*/
public function validate(
$password,
$secret,
$currentCounter,
$window = null,
&$newCounter = null
&$newCounter = null,
$window = null
) {
if (null === $window) {
$window = 0;
Expand Down Expand Up @@ -94,17 +94,17 @@ public function validate(
* @param array<string> $passwords The password sequence to validate.
* @param string $secret The HOTP secret.
* @param integer $currentCounter The current counter value.
* @param integer|null $window The amount of counter increments to search through for a match.
* @param integer|null &$newCounter Will be set to the new counter value.
* @param integer|null $window The amount of counter increments to search through for a match.
*
* @return boolean True if the password is valid.
*/
public function validateSequence(
array $passwords,
$secret,
$currentCounter,
$window = null,
&$newCounter = null
&$newCounter = null,
$window = null
) {
$newCounter = $currentCounter;

Expand All @@ -117,14 +117,12 @@ public function validateSequence(
array_shift($passwords),
$secret,
$currentCounter,
$window,
$counter
$counter,
$window
)
) {
foreach ($passwords as $password) {
if (
!$this->validate($password, $secret, $counter, 0, $counter)
) {
if (!$this->validate($password, $secret, $counter, $counter)) {
return false;
}
}
Expand Down
12 changes: 6 additions & 6 deletions src/Eloquent/Otis/Hotp/HotpValidatorInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,17 @@ interface HotpValidatorInterface
* @param string $password The password to validate.
* @param string $secret The HOTP secret.
* @param integer $currentCounter The current counter value.
* @param integer|null $window The amount of counter increments to search through for a match.
* @param integer|null &$newCounter Will be set to the new counter value.
* @param integer|null $window The amount of counter increments to search through for a match.
*
* @return boolean True if the password is valid.
*/
public function validate(
$password,
$secret,
$currentCounter,
$window = null,
&$newCounter = null
&$newCounter = null,
$window = null
);

/**
Expand All @@ -41,16 +41,16 @@ public function validate(
* @param array<string> $passwords The password sequence to validate.
* @param string $secret The HOTP secret.
* @param integer $currentCounter The current counter value.
* @param integer|null $window The amount of counter increments to search through for a match.
* @param integer|null &$newCounter Will be set to the new counter value.
* @param integer|null $window The amount of counter increments to search through for a match.
*
* @return boolean True if the password is valid.
*/
public function validateSequence(
array $passwords,
$secret,
$currentCounter,
$window = null,
&$newCounter = null
&$newCounter = null,
$window = null
);
}
Loading

0 comments on commit 9b52f4f

Please sign in to comment.