Skip to content

Commit

Permalink
Add tests, readme, packagist
Browse files Browse the repository at this point in the history
  • Loading branch information
kayon-ariel committed Oct 13, 2024
1 parent e917a43 commit f96fcd6
Show file tree
Hide file tree
Showing 7 changed files with 293 additions and 60 deletions.
121 changes: 89 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,52 +1,109 @@
Time-based one-time password PHP class
==============================
# TOTP-PHP

Original: PHPGangsta/GoogleAuthenticator
* Copyright (c) 2012-2016, [http://www.phpgangsta.de](http://www.phpgangsta.de)
* Author: Michael Kliewe, [@PHPGangsta](http://twitter.com/PHPGangsta) and [contributors](https://github.com/PHPGangsta/GoogleAuthenticator/graphs/contributors)
* Licensed under the BSD License.
![PHP Version](https://img.shields.io/badge/php-%3E%3D8.2-brightgreen) ![License](https://img.shields.io/badge/license-MIT-blue) [![Packagist Downloads](https://img.shields.io/packagist/dm/kayon-ariel/totp-php.svg?label=Packagist%20downloads)](https://packagist.org/packages/kayon-ariel/totp-php)

## Introduction

**TOTP-PHP** is a PHP library for generating Time-based One-Time Passwords (TOTP) for two-factor authentication. This library is easy to integrate into your existing PHP applications, allowing you to enhance your security measures effectively.

**TOTP-PHP** is compatible with **Google Authenticator** and other TOTP applications, making it a great choice for implementing two-factor authentication in your projects.

For a secure installation you have to make sure that used codes cannot be reused (replay-attack). You also need to limit the number of verifications, to fight against brute-force attacks. For example you could limit the amount of verifications to 10 tries within 10 minutes for one IP address (or IPv6 block). It depends on your environment.

This PHP class can be used to interact with the Google Authenticator mobile app for 2-factor-authentication. This class
can generate secrets, generate codes, validate codes and present a QR-Code for scanning the secret. It implements TOTP
according to [RFC6238](https://tools.ietf.org/html/rfc6238)
## Features

For a secure installation you have to make sure that used codes cannot be reused (replay-attack). You also need to
limit the number of verifications, to fight against brute-force attacks. For example you could limit the amount of
verifications to 10 tries within 10 minutes for one IP address (or IPv6 block). It depends on your environment.
- Generate TOTP codes compliant with RFC 6238.
- Simple for use.
- Built-in validation methods.
- Secret key generation for TOTP.
- Compatible with Google Authenticator and similar apps.

Usage:
------
## Installation

See following example:
You can install the `totp-php` library via Composer. Run the following command in your terminal:

```bash
composer require kayon-ariel/totp-php
```
See the example in the `example.php` file

## Usage

Here is a simple example of how to use the library:

### Generating a TOTP Code

```php
require 'vendor/autoload.php';

use TotpPhp\Totp;

// Create a new TOTP instance
$ga = new Totp();

// Generate a secret key
$secret = $ga->createSecret();
echo "Secret is: " . $secret . "\n\n";

// Generate a TOTP code based on the secret
$oneCode = $ga->getCode($secret);
echo "Checking Code '$oneCode' and Secret '$secret':\n";

// Verify the TOTP code against the secret
$checkResult = $ga->verifyCode($secret, $oneCode);

if ($checkResult) {
echo 'OK';
} else {
echo 'FAILED';
}
```

Running the script provides the following output:
### Generating a Secret Key

You can generate a random secret key using the `createSecret` method. This is useful for initializing a new user or session.

```php
$secret = $ga->createSecret();
echo "Your secret key is: " . $secret;
```
Secret is: OQB6ZZGYHCPSX4AK

Checking Code '848634' and Secret 'OQB6ZZGYHCPSX4AK':
OK
### Validating a TOTP Code

You can validate a TOTP code using the `verifyCode` method:

```php
$userInputCode = '123456'; // Example user input
$isValid = $ga->verifyCode($secret, $userInputCode);

if ($isValid) {
echo "The TOTP code is valid!";
} else {
echo "The TOTP code is invalid!";
}
```

Installation:
-------------
### Code Generation and Verification Logic

The library uses the following methods:

- **`createSecret(int $secretLength = 16): string`**: Generates a new secret key with a specified length (minimum 16 characters).
- **`getCode(string $secret, ?int $timeSlice = null): string`**: Calculates the TOTP code for a given secret key and time slice (defaults to the current time).
- **`verifyCode(string $secret, string $code, int $discrepancy = 1): bool`**: Checks if the provided TOTP code matches the expected code for the secret, allowing for some time discrepancy.

## License

- Use [Composer](https://getcomposer.org/doc/01-basic-usage.md) to
install the package
This library incorporates code from the original `PHPGangsta/GoogleAuthenticator` project:

- From project root directory execute following
* Copyright (c) 2012-2016, [http://www.phpgangsta.de](http://www.phpgangsta.de)
* Author: Michael Kliewe, [@PHPGangsta](http://twitter.com/PHPGangsta) and [contributors](https://github.com/PHPGangsta/GoogleAuthenticator/graphs/contributors)
* Licensed under the BSD License.

```composer install```
Current: Copyright (c) 2024 Kayon Ariel, provided under the MIT License.

- [Composer](https://getcomposer.org/doc/01-basic-usage.md) will take care of autoloading
the library. Just include the following at the top of your file
## Contributions

`require_once __DIR__ . '/../vendor/autoload.php';`
Contributions are welcome! Feel free to submit issues, fork the repository, and submit pull requests.

Notes:
------
## Contact

If you like this script or have some features to add: contact me, visit my blog, fork this project, send pull requests, you know how it works.
For any inquiries or feedback, you can reach out to me at [[email protected]].
46 changes: 26 additions & 20 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,30 +1,36 @@
{
"name": "kayon-ariel/totpphp",
"name": "kayon-ariel/totp-php",
"description": "Google Authenticator 2-factor authentication",
"version": "1.0.0",
"keywords": [
"GoogleAuthenticator",
"TOTP",
"rfc6238",
"otp",
"2-factor"
],
"type": "library",
"keywords": ["GoogleAuthenticator", "TOTP", "rfc6238", "otp", "2-factor"],
"license": "BSD-4-Clause",
"license": "MIT",
"autoload": {
"psr-4": {
"TotpPhp\\": "src/"
}
},
"require": {
"php": ">=8.2"
},
"require-dev": {
"phpunit/phpunit": "^9.5"
},
"support": {
"source": "https://github.com/kayon-ariel/totp-php",
"issues": "https://github.com/kayon-ariel/totp-php/issues"
},
"authors": [
{
"name": "Kayon Ariel",
"email": "[email protected]",
"homepage": "https://github.com/kayon-ariel",
"role": "Developer"
}
],
"support": {
"source": "https://github.com/kayon-ariel/totp-php",
"issues": "https://github.com/kayon-ariel/totp-php/issues"
},
"require": {
"php": ">=8.2"
},
"autoload": {
"classmap": ["src/Totp.php"]
}
}




]
}
15 changes: 12 additions & 3 deletions example.php
Original file line number Diff line number Diff line change
@@ -1,16 +1,25 @@
<?php
require_once 'src/Totp.php';

require 'vendor/autoload.php';

use TotpPhp\Totp;

// Create a new TOTP instance
$ga = new Totp();

// Generate a secret key
$secret = $ga->createSecret();
echo "Secret is: ".$secret."\n\n";
echo "Secret is: " . $secret . "\n\n";

// Generate a TOTP code based on the secret
$oneCode = $ga->getCode($secret);
echo "Checking Code '$oneCode' and Secret '$secret':\n";

// Verify the TOTP code against the secret
$checkResult = $ga->verifyCode($secret, $oneCode);

if ($checkResult) {
echo 'OK';
} else {
echo 'FAILED';
}
}
45 changes: 41 additions & 4 deletions src/Base32.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

namespace TotpPhp;

/**
* PHP Class for handling base32.
*/
Expand All @@ -21,6 +23,12 @@ public function _base32Decode($secret)
$base32chars = $this->_getBase32LookupTable();
$base32charsFlipped = array_flip($base32chars);

foreach (str_split($secret) as $char) {
if (!isset($base32charsFlipped[$char]) && $char !== '=') {
return false;
}
}

$paddingCharCount = substr_count($secret, $base32chars[32]);
$allowedValues = array(6, 4, 3, 1, 0);
if (!in_array($paddingCharCount, $allowedValues)) {
Expand All @@ -34,6 +42,7 @@ public function _base32Decode($secret)
return false;
}
}

$secret = str_replace('=', '', $secret);
$secret = str_split($secret);
$binaryString = '';
Expand Down Expand Up @@ -62,10 +71,38 @@ public function _base32Decode($secret)
public function _getBase32LookupTable()
{
return array(
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 7
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 15
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 23
'Y', 'Z', '2', '3', '4', '5', '6', '7', // 31
'A',
'B',
'C',
'D',
'E',
'F',
'G',
'H', // 7
'I',
'J',
'K',
'L',
'M',
'N',
'O',
'P', // 15
'Q',
'R',
'S',
'T',
'U',
'V',
'W',
'X', // 23
'Y',
'Z',
'2',
'3',
'4',
'5',
'6',
'7', // 31
'=', // padding char
);
}
Expand Down
2 changes: 1 addition & 1 deletion src/Totp.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

require_once 'Base32.php';
namespace TotpPhp;

/**
* PHP Class for handling Time-based one-time password.
Expand Down
39 changes: 39 additions & 0 deletions tests/Base32Test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

use PHPUnit\Framework\TestCase;
use TotpPhp\Base32;

class Base32Test extends TestCase
{
protected $base32;

protected function setUp(): void
{
$this->base32 = new Base32();
}

public function testBase32Decode()
{
$encoded = 'JBSWY3DP';
$decoded = $this->base32->_base32Decode($encoded);

$this->assertNotFalse($decoded);
$this->assertEquals('Hello', $decoded);
}

public function testBase32DecodeInvalid()
{
$invalidEncoded = 'INVALIDBAS2251STRING';
$decoded = $this->base32->_base32Decode($invalidEncoded);

$this->assertFalse($decoded, 'Base32 decoding of an invalid string should return false.');
}


public function testGetBase32LookupTable()
{
$lookupTable = $this->base32->_getBase32LookupTable();
$this->assertIsArray($lookupTable);
$this->assertCount(33, $lookupTable);
}
}
Loading

0 comments on commit f96fcd6

Please sign in to comment.