diff options
Diffstat (limited to 'vendor/christian-riesen')
23 files changed, 1572 insertions, 0 deletions
diff --git a/vendor/christian-riesen/base32/.gitignore b/vendor/christian-riesen/base32/.gitignore new file mode 100644 index 00000000..23f1f99d --- /dev/null +++ b/vendor/christian-riesen/base32/.gitignore @@ -0,0 +1,3 @@ +composer.lock +build/ +vendor/ diff --git a/vendor/christian-riesen/base32/.scrutinizer.yml b/vendor/christian-riesen/base32/.scrutinizer.yml new file mode 100644 index 00000000..cf8b9954 --- /dev/null +++ b/vendor/christian-riesen/base32/.scrutinizer.yml @@ -0,0 +1,14 @@ +before_commands: + - "composer install --prefer-dist" + +tools: + php_mess_detector: true + php_code_sniffer: true + php_analyzer: true + sensiolabs_security_checker: true + php_code_coverage: true + php_cpd: true + php_pdepend: + excluded_dirs: [vendor/, tests/*] +filter: + excluded_paths: [vendor/] diff --git a/vendor/christian-riesen/base32/.travis.yml b/vendor/christian-riesen/base32/.travis.yml new file mode 100644 index 00000000..0b0505e6 --- /dev/null +++ b/vendor/christian-riesen/base32/.travis.yml @@ -0,0 +1,19 @@ +language: php + +php: + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - hhvm + +before_script: + - composer install + +before_script: + - curl -s http://getcomposer.org/installer | php + - php composer.phar update --dev --no-interaction + - mkdir -p build/logs + +script: + - php vendor/bin/phpunit --coverage-text diff --git a/vendor/christian-riesen/base32/LICENSE b/vendor/christian-riesen/base32/LICENSE new file mode 100644 index 00000000..624fceb1 --- /dev/null +++ b/vendor/christian-riesen/base32/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2013-2014 Christian Riesen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/christian-riesen/base32/README.md b/vendor/christian-riesen/base32/README.md new file mode 100644 index 00000000..00d693d6 --- /dev/null +++ b/vendor/christian-riesen/base32/README.md @@ -0,0 +1,60 @@ +base32 +====== + +Base32 Encoder/Decoder for PHP according to RFC 4648 + +[![Build Status](https://secure.travis-ci.org/ChristianRiesen/base32.png)](http://travis-ci.org/ChristianRiesen/base32) +[![HHVM Status](http://hhvm.h4cc.de/badge/christian-riesen/base32.png)](http://hhvm.h4cc.de/package/christian-riesen/base32) + +[![Latest Stable Version](https://poser.pugx.org/christian-riesen/base32/v/stable.png)](https://packagist.org/packages/christian-riesen/base32) [![Total Downloads](https://poser.pugx.org/christian-riesen/base32/downloads.png)](https://packagist.org/packages/christian-riesen/base32) [![Latest Unstable Version](https://poser.pugx.org/christian-riesen/base32/v/unstable.png)](https://packagist.org/packages/christian-riesen/base32) [![License](https://poser.pugx.org/christian-riesen/base32/license.png)](https://packagist.org/packages/christian-riesen/base32) + +Do you like this? Flattr it: + +[![Flattr base32](http://api.flattr.com/button/flattr-badge-large.png)](http://flattr.com/thing/720563/ChristianRiesenbase32-on-GitHub) + +Usage +----- + + <?php + + // Include class or user autoloader + use Base32\Base32; + + $string = 'fooba'; + + $encoded = Base32::encode($string); + // $encoded contains now 'MZXW6YTB' + + $decoded = Base32::decode($encoded); + // $decoded is again 'fooba' + + +About +===== + +Use +--- + +Initially created to work with the [one time password project](https://github.com/ChristianRiesen/otp), yet it can stand alone just as well as [Jordi Boggiano](http://seld.be/) kindly pointed out. It's the only Base32 implementation I could make work that passes the test vectors (and contains unit tests). + +Goal +---- +Have a RFC compliant Base32 encoder and decoder. The implementation could be improved, but for now, it does the job and has unit tests. Ideally, the class can be enhanced while the unit tests keep passing. + +Requirements +------------ + +PHP 5.3.x+ + +If you want to run the tests, PHPUnit 3.6 or up is required. + +Author +------ + +Christian Riesen <chris.riesen@gmail.com> http://christianriesen.com + +Acknowledgements +---------------- + +Base32 is mostly based on the work of https://github.com/NTICompass/PHP-Base32 + diff --git a/vendor/christian-riesen/base32/build.xml b/vendor/christian-riesen/base32/build.xml new file mode 100644 index 00000000..c06a49f9 --- /dev/null +++ b/vendor/christian-riesen/base32/build.xml @@ -0,0 +1,130 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project name="christianriesen-base32" default="build"> + <target name="build" depends="prepare,lint,phploc,pdepend,phpmd-ci,phpcs-ci,phpcpd,phpunit,phpcb"/> + + <target name="build-parallel" depends="prepare,lint,tools-parallel,phpunit,phpcb"/> + + <target name="tools-parallel" description="Run tools in parallel"> + <parallel threadCount="2"> + <sequential> + <antcall target="pdepend"/> + <antcall target="phpmd-ci"/> + </sequential> + <antcall target="phpcpd"/> + <antcall target="phpcs-ci"/> + <antcall target="phploc"/> + </parallel> + </target> + + <target name="clean" description="Cleanup build artifacts"> + <delete dir="${basedir}/build/code-browser"/> + <delete dir="${basedir}/build/coverage"/> + <delete dir="${basedir}/build/logs"/> + <delete dir="${basedir}/build/pdepend"/> + <exec executable="bash"> + <arg value="-c" /> + <arg value="curl -s http://getcomposer.org/installer | php" /> + </exec> + <exec executable="php"> + <arg value="composer.phar" /> + <arg value="install" /> + </exec> + </target> + + <target name="prepare" depends="clean" description="Prepare for build"> + <mkdir dir="${basedir}/build/code-browser"/> + <mkdir dir="${basedir}/build/coverage"/> + <mkdir dir="${basedir}/build/logs"/> + <mkdir dir="${basedir}/build/pdepend"/> + </target> + + <target name="lint" description="Perform syntax check of sourcecode files"> + <apply executable="php" failonerror="true"> + <arg value="-l"/> + + <fileset dir="${basedir}/src"> + <include name="**/*.php"/> + <modified/> + </fileset> + + <fileset dir="${basedir}/tests"> + <include name="**/*.php"/> + <modified/> + </fileset> + </apply> + </target> + + <target name="phploc" description="Measure project size using PHPLOC"> + <exec executable="phploc"> + <arg value="--log-csv"/> + <arg value="${basedir}/build/logs/phploc.csv"/> + <arg path="${basedir}/src"/> + </exec> + </target> + + <target name="pdepend" description="Calculate software metrics using PHP_Depend"> + <exec executable="pdepend"> + <arg value="--jdepend-xml=${basedir}/build/logs/jdepend.xml"/> + <arg value="--jdepend-chart=${basedir}/build/pdepend/dependencies.svg"/> + <arg value="--overview-pyramid=${basedir}/build/pdepend/overview-pyramid.svg"/> + <arg path="${basedir}/src"/> + </exec> + </target> + + <target name="phpmd" description="Perform project mess detection using PHPMD and print human readable output. Intended for usage on the command line before committing."> + <exec executable="phpmd"> + <arg path="${basedir}/src"/> + <arg value="text"/> + <arg value="${basedir}/build/phpmd.xml"/> + </exec> + </target> + + <target name="phpmd-ci" description="Perform project mess detection using PHPMD creating a log file for the continuous integration server"> + <exec executable="phpmd"> + <arg path="${basedir}/src"/> + <arg value="xml"/> + <arg value="${basedir}/build/phpmd.xml"/> + <arg value="--reportfile"/> + <arg value="${basedir}/build/logs/pmd.xml"/> + </exec> + </target> + + <target name="phpcs" description="Find coding standard violations using PHP_CodeSniffer and print human readable output. Intended for usage on the command line before committing."> + <exec executable="phpcs"> + <arg value="--standard=${basedir}/build/phpcs.xml"/> + <arg path="${basedir}/src"/> + </exec> + </target> + + <target name="phpcs-ci" description="Find coding standard violations using PHP_CodeSniffer creating a log file for the continuous integration server"> + <exec executable="phpcs" output="/dev/null"> + <arg value="--report=checkstyle"/> + <arg value="--report-file=${basedir}/build/logs/checkstyle.xml"/> + <arg value="--standard=${basedir}/build/phpcs.xml"/> + <arg path="${basedir}/src"/> + </exec> + </target> + + <target name="phpcpd" description="Find duplicate code using PHPCPD"> + <exec executable="phpcpd"> + <arg value="--log-pmd"/> + <arg value="${basedir}/build/logs/pmd-cpd.xml"/> + <arg path="${basedir}/src"/> + </exec> + </target> + + <target name="phpunit" description="Run unit tests with PHPUnit"> + <exec executable="phpunit" failonerror="true"/> + </target> + + <target name="phpcb" description="Aggregate tool output with PHP_CodeBrowser"> + <exec executable="phpcb"> + <arg value="--log"/> + <arg path="${basedir}/build/logs"/> + <arg value="--source"/> + <arg path="${basedir}/src"/> + <arg value="--output"/> + <arg path="${basedir}/build/code-browser"/> + </exec> + </target> +</project> diff --git a/vendor/christian-riesen/base32/composer.json b/vendor/christian-riesen/base32/composer.json new file mode 100644 index 00000000..0c61fbf7 --- /dev/null +++ b/vendor/christian-riesen/base32/composer.json @@ -0,0 +1,33 @@ +{ + "name": "christian-riesen/base32", + "type": "library", + "description": "Base32 encoder/decoder according to RFC 4648", + "keywords": ["base32","encode","decode","rfc4648"], + "homepage": "https://github.com/ChristianRiesen/base32", + "license": "MIT", + "authors": [ + { + "name": "Christian Riesen", + "email": "chris.riesen@gmail.com", + "homepage": "http://christianriesen.com", + "role": "Developer" + } + ], + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "4.*", + "satooshi/php-coveralls": "0.*" + }, + "autoload": { + "psr-4": { + "Base32\\": "src/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + } +} diff --git a/vendor/christian-riesen/base32/phpunit.xml.dist b/vendor/christian-riesen/base32/phpunit.xml.dist new file mode 100644 index 00000000..3e2def6f --- /dev/null +++ b/vendor/christian-riesen/base32/phpunit.xml.dist @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<phpunit + backupGlobals="false" + backupStaticAttributes="false" + convertErrorsToExceptions="true" + convertNoticesToExceptions="true" + convertWarningsToExceptions="true" + processIsolation="false" + stopOnFailure="false" + syntaxCheck="false" + bootstrap="tests/bootstrap.php" + colors="true"> + <testsuites> + <testsuite name="Base32 Test Suite"> + <directory suffix="Test.php">tests/</directory> + </testsuite> + </testsuites> + + <filter> + <whitelist> + <directory suffix=".php">src/</directory> + </whitelist> + </filter> + + <logging> + <log type="coverage-html" target="build/coverage" title="Base32" + charset="UTF-8" yui="true" highlight="true" + lowUpperBound="35" highLowerBound="70"/> + <log type="coverage-clover" target="build/logs/clover.xml"/> + <log type="junit" target="build/logs/junit.xml" logIncompleteSkipped="false"/> + </logging> +</phpunit> diff --git a/vendor/christian-riesen/base32/src/Base32.php b/vendor/christian-riesen/base32/src/Base32.php new file mode 100644 index 00000000..bf790182 --- /dev/null +++ b/vendor/christian-riesen/base32/src/Base32.php @@ -0,0 +1,146 @@ +<?php +namespace Base32; + +/** + * Base32 encoder and decoder + * + * Last update: 2012-06-20 + * + * RFC 4648 compliant + * @link http://www.ietf.org/rfc/rfc4648.txt + * + * Some groundwork based on this class + * https://github.com/NTICompass/PHP-Base32 + * + * @author Christian Riesen <chris.riesen@gmail.com> + * @link http://christianriesen.com + * @license MIT License see LICENSE file + */ +class Base32 +{ + /** + * Alphabet for encoding and decoding base32 + * + * @var array + */ + private static $alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567='; + + /** + * Creates an array from a binary string into a given chunk size + * + * @param string $binaryString String to chunk + * @param integer $bits Number of bits per chunk + * @return array + */ + private static function chunk($binaryString, $bits) + { + $binaryString = chunk_split($binaryString, $bits, ' '); + + if (substr($binaryString, (strlen($binaryString)) - 1) == ' ') { + $binaryString = substr($binaryString, 0, strlen($binaryString)-1); + } + + return explode(' ', $binaryString); + } + + /** + * Encodes into base32 + * + * @param string $string Clear text string + * @return string Base32 encoded string + */ + public static function encode($string) + { + if (strlen($string) == 0) { + // Gives an empty string + + return ''; + } + + // Convert string to binary + $binaryString = ''; + + foreach (str_split($string) as $s) { + // Return each character as an 8-bit binary string + $binaryString .= sprintf('%08b', ord($s)); + } + + // Break into 5-bit chunks, then break that into an array + $binaryArray = self::chunk($binaryString, 5); + + // Pad array to be divisible by 8 + while (count($binaryArray) % 8 !== 0) { + $binaryArray[] = null; + } + + $base32String = ''; + + // Encode in base32 + foreach ($binaryArray as $bin) { + $char = 32; + + if (!is_null($bin)) { + // Pad the binary strings + $bin = str_pad($bin, 5, 0, STR_PAD_RIGHT); + $char = bindec($bin); + } + + // Base32 character + $base32String .= self::$alphabet[$char]; + } + + return $base32String; + } + + /** + * Decodes base32 + * + * @param string $base32String Base32 encoded string + * @return string Clear text string + */ + public static function decode($base32String) + { + // Only work in upper cases + $base32String = strtoupper($base32String); + + // Remove anything that is not base32 alphabet + $pattern = '/[^A-Z2-7]/'; + + $base32String = preg_replace($pattern, '', $base32String); + + if (strlen($base32String) == 0) { + // Gives an empty string + return ''; + } + + $base32Array = str_split($base32String); + + $string = ''; + + foreach ($base32Array as $str) { + $char = strpos(self::$alphabet, $str); + + // Ignore the padding character + if ($char !== 32) { + $string .= sprintf('%05b', $char); + } + } + + while (strlen($string) %8 !== 0) { + $string = substr($string, 0, strlen($string)-1); + } + + $binaryArray = self::chunk($string, 8); + + $realString = ''; + + foreach ($binaryArray as $bin) { + // Pad each value to 8 bits + $bin = str_pad($bin, 8, 0, STR_PAD_RIGHT); + // Convert binary strings to ASCII + $realString .= chr(bindec($bin)); + } + + return $realString; + } +} diff --git a/vendor/christian-riesen/base32/tests/Base32Test.php b/vendor/christian-riesen/base32/tests/Base32Test.php new file mode 100644 index 00000000..3e5924ce --- /dev/null +++ b/vendor/christian-riesen/base32/tests/Base32Test.php @@ -0,0 +1,51 @@ +<?php + +namespace Base32; + +use Base32\Base32; + +/** + * Base32 test case. + */ +class Base32Test extends \PHPUnit_Framework_TestCase +{ + /** + * Tests Base32->decode() + * + * Testing test vectors according to RFC 4648 + * http://www.ietf.org/rfc/rfc4648.txt + */ + public function testDecode() + { + // RFC test vectors say that empty string returns empty string + $this->assertEquals('', Base32::decode('')); + + // these strings are taken from the RFC + $this->assertEquals('f', Base32::decode('MY======')); + $this->assertEquals('fo', Base32::decode('MZXQ====')); + $this->assertEquals('foo', Base32::decode('MZXW6===')); + $this->assertEquals('foob', Base32::decode('MZXW6YQ=')); + $this->assertEquals('fooba', Base32::decode('MZXW6YTB')); + $this->assertEquals('foobar', Base32::decode('MZXW6YTBOI======')); + + // Decoding a string made up entirely of invalid characters + $this->assertEquals('', Base32::decode('8908908908908908')); + } + + /** + * Encoder tests, reverse of the decodes + */ + public function testEncode() + { + // RFC test vectors say that empty string returns empty string + $this->assertEquals('', Base32::encode('')); + + // these strings are taken from the RFC + $this->assertEquals('MY======', Base32::encode('f')); + $this->assertEquals('MZXQ====', Base32::encode('fo')); + $this->assertEquals('MZXW6===', Base32::encode('foo')); + $this->assertEquals('MZXW6YQ=', Base32::encode('foob')); + $this->assertEquals('MZXW6YTB', Base32::encode('fooba')); + $this->assertEquals('MZXW6YTBOI======', Base32::encode('foobar')); + } +} diff --git a/vendor/christian-riesen/base32/tests/bootstrap.php b/vendor/christian-riesen/base32/tests/bootstrap.php new file mode 100644 index 00000000..12bea5b1 --- /dev/null +++ b/vendor/christian-riesen/base32/tests/bootstrap.php @@ -0,0 +1,5 @@ +<?php + +$loader = require __DIR__ . '/../vendor/autoload.php'; +$loader->add("Base32", __DIR__); +$loader->register(); diff --git a/vendor/christian-riesen/otp/.gitignore b/vendor/christian-riesen/otp/.gitignore new file mode 100644 index 00000000..bab62331 --- /dev/null +++ b/vendor/christian-riesen/otp/.gitignore @@ -0,0 +1,5 @@ +.svn +/.buildpath +/.project +/.settings + diff --git a/vendor/christian-riesen/otp/.travis.yml b/vendor/christian-riesen/otp/.travis.yml new file mode 100644 index 00000000..b863299e --- /dev/null +++ b/vendor/christian-riesen/otp/.travis.yml @@ -0,0 +1,11 @@ +language: php + +php: + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - 7.0 + +script: phpunit + diff --git a/vendor/christian-riesen/otp/LICENSE b/vendor/christian-riesen/otp/LICENSE new file mode 100644 index 00000000..70894746 --- /dev/null +++ b/vendor/christian-riesen/otp/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) Christian Riesen http://christianriesen.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/vendor/christian-riesen/otp/README.md b/vendor/christian-riesen/otp/README.md new file mode 100644 index 00000000..c979dbbc --- /dev/null +++ b/vendor/christian-riesen/otp/README.md @@ -0,0 +1,105 @@ +One Time Passwords +================== + +[![Build Status](https://secure.travis-ci.org/ChristianRiesen/otp.png)](http://travis-ci.org/ChristianRiesen/otp) + +Did you like this? Flattr it: + +[![Flattr otp](http://api.flattr.com/button/flattr-badge-large.png)](https://flattr.com/thing/719284/ChristianRiesenotp-on-GitHub) + +Installation +------------ + +Use [composer](http://getcomposer.org/) and require the library in your `composer.json` + + { + "require": { + "christian-riesen/otp": "1.*", + } + } + +Usage +----- + +```php +<?php + +use Otp\Otp; +use Otp\GoogleAuthenticator; + +// Seperate class, see https://github.com/ChristianRiesen/base32 +use Base32\Base32; + +// Get a Pseudo Secret +// Defaults to 16 characters +$secret = GoogleAuthenticator::generateRandom(); + +// Url for the QR code +// Using totp method +$url = GoogleAuthenticator::getQrCodeUrl('totp', 'Label like user@host.com', $secret); + +// Save the secret with the users account +// Display QR Code to the user + +// Now how to check +$otp = new Otp(); + +// $key is a 6 digit number, coming from the User +// Assuming this is present and sanitized +// Allows for a 1 code time drift by default +// Third parameter can alter that behavior +if ($otp->checkTotp(Base32::decode($secret), $key)) { + // Correct key + // IMPORTANT! Note this key as being used + // so nobody could launch a replay attack. + // Cache that for the next minutes and you + // should be good. +} else { + // Wrong key +} + +// Just to create a key for display (testing) +$key = $otp->totp($secret); + +``` + +Sample script in `example` folder. Requires sessions to work (for secret storage). + +Class Otp +--------- + +Implements hotp according to [RFC4226](https://tools.ietf.org/html/rfc4226) and totp according to [RFC6238](https://tools.ietf.org/html/rfc6238) (only sha1 algorithm). Once you have a secret, you can use it directly in this class to create the passwords themselves (mainly for debugging use) or use the check functions to safely check the validity of the keys. The `checkTotp` function also includes a helper to battle timedrift. + +Class GoogleAuthenticator +------------------------- + +Static function class to generate a correct url for the QR code, so you can easy scan it with your device. Google Authenticator is avaiaible as application for iPhone and Android. This removes the burden to create such an app from the developers of websites by using this set of classes. + +There are also older open source versions of the Google Authenticator app for both [iPhone](https://github.com/google/google-authenticator) and [Android](https://github.com/google/google-authenticator-android) + +This helper class uses the random_int function from PHP7, or the polyfill method from [paragonie/random_compat](https://packagist.org/packages/paragonie/random_compat) if present and falls back on other (less "secure") random generators. + +About +===== + +Requirements +------------ + +PHP 5.3.x+ + +Uses [Base32 class](https://github.com/ChristianRiesen/base32). + +If you want to run the tests, PHPUnit 3.6 or up is required. + +Author +------ + +Christian Riesen <chris.riesen@gmail.com> http://christianriesen.com + +Acknowledgements +---------------- + +The classes have been inspired by many different places that were talking about otp and Google Authenticator. Thank you all for your help. + +Project setup ideas blantently taken from https://github.com/Seldaek/monolog + diff --git a/vendor/christian-riesen/otp/composer.json b/vendor/christian-riesen/otp/composer.json new file mode 100644 index 00000000..94f16c67 --- /dev/null +++ b/vendor/christian-riesen/otp/composer.json @@ -0,0 +1,28 @@ +{ + "name": "christian-riesen/otp", + "type": "library", + "description": "One Time Passwords, hotp and totp according to RFC4226 and RFC6238", + "keywords": ["otp","hotp","totp","googleauthenticator","rfc4226","rfc6238"], + "homepage": "https://github.com/ChristianRiesen/otp", + "license": "MIT", + "authors": [ + { + "name": "Christian Riesen", + "email": "chris.riesen@gmail.com", + "homepage": "http://christianriesen.com", + "role": "Developer" + } + ], + "require": { + "php": ">=5.3.0", + "christian-riesen/base32": ">=1.0" + }, + "suggest": { + "paragonie/random_compat": "Optional polyfill for a more secure random generator for pre PHP7 versions" + }, + "autoload": { + "psr-0": { + "Otp": "src" + } + } +} diff --git a/vendor/christian-riesen/otp/example/index.php b/vendor/christian-riesen/otp/example/index.php new file mode 100644 index 00000000..4da362c5 --- /dev/null +++ b/vendor/christian-riesen/otp/example/index.php @@ -0,0 +1,100 @@ +<?php + +session_start(); // using it as storage temporary + +require_once __DIR__ . '/../vendor/autoload.php'; + +use Otp\Otp; +use Otp\GoogleAuthenticator; +use Base32\Base32; + +// Getting a secret, either by generating or from storage +// DON'T use sessions as storage for this in production!!! +$secret = 0; + +if (isset($_SESSION['otpsecret'])) { + $secret = $_SESSION['otpsecret']; +} + +if (strlen($secret) != 16) { + $secret = GoogleAuthenticator::generateRandom(); + $_SESSION['otpsecret'] = $secret; +} + +// The secret is now an easy stored Base32 string. +// To use it in totp though we need to decode it into the original +$otp = new Otp(); + +$currentTotp = $otp->totp(Base32::decode($secret)); + +$qrCode = GoogleAuthenticator::getQrCodeUrl('totp', 'otpsample@cr', $secret); +$keyUri = GoogleAuthenticator::getKeyUri('totp', 'otpsample@cr', $secret); + +?><html> +<head> +<title>One Time Passwords Example</title> +</head> +<body> + +<h1>One Time Passwords Example</h1> + +Secret is <?php echo $secret; ?>. This is saved with the users credentials. +<br /> +<br /> +<hr /> + +QR Code for totp:<br /> +<img src="<?php echo $qrCode; ?>" /> +<br /> +This QR Code contains the Key URI: <?php echo $keyUri; ?> +<br /> +<hr /> + +Current totp would be <?php echo $currentTotp; ?><br /> +<br /> +<hr /> + +Because of timedrift, you could technically enter a code before or after it +would actually be used. This form uses the checkTotp function. To test this, +open this page, wait until the key changes once or twice (not more) on your +Google Authenticator, then hit submit. Even though the key is "wrong" because of +small time differences, you can still use it. +<form action="" method="post"> +<input type="text" name="otpkey" value="<?php echo $currentTotp; ?>" /><br /> +<input type="submit"> +</form> + +<br /> +Output:<br /> +<br /> + + +<?php + +if (isset($_POST['otpkey'])) { + // Sanatizing, this should take care of it + $key = preg_replace('/[^0-9]/', '', $_POST['otpkey']); + + // Standard is 6 for keys, but can be changed with setDigits on $otp + if (strlen($key) == 6) { + // Remember that the secret is a base32 string that needs decoding + // to use it here! + if ($otp->checkTotp(Base32::decode($secret), $key)) { + echo 'Key correct!'; + // Add here something that makes note of this key and will not allow + // the use of it, for this user for the next 2 minutes. This way you + // prevent a replay attack. Otherwise your OTP is missing one of the + // key features it can bring in security to your application! + } else { + echo 'Wrong key!'; + } + + } else { + echo 'Key not the correct size'; + } +} + +?> + +</body> +</html> diff --git a/vendor/christian-riesen/otp/phpunit.xml.dist b/vendor/christian-riesen/otp/phpunit.xml.dist new file mode 100644 index 00000000..7ffc6c63 --- /dev/null +++ b/vendor/christian-riesen/otp/phpunit.xml.dist @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<phpunit colors="true"> + <testsuites> + <testsuite name="Otp Test Suite"> + <directory>tests/Otp/</directory> + </testsuite> + </testsuites> + + <filter> + <whitelist> + <directory suffix=".php">src/Otp/</directory> + </whitelist> + </filter> +</phpunit> diff --git a/vendor/christian-riesen/otp/src/Otp/GoogleAuthenticator.php b/vendor/christian-riesen/otp/src/Otp/GoogleAuthenticator.php new file mode 100644 index 00000000..23e67ff0 --- /dev/null +++ b/vendor/christian-riesen/otp/src/Otp/GoogleAuthenticator.php @@ -0,0 +1,189 @@ +<?php +namespace Otp; + +/** + * Google Authenticator + * + * Last update: 2014-08-19 + * + * Can be easy used with Google Authenticator + * @link https://code.google.com/p/google-authenticator/ + * + * @author Christian Riesen <chris.riesen@gmail.com> + * @link http://christianriesen.com + * @license MIT License see LICENSE file + */ + +class GoogleAuthenticator +{ + protected static $allowedTypes = array('hotp', 'totp'); + + protected static $height = 200; + protected static $width = 200; + + /** + * Returns the Key URI + * + * Format of encoded url is here: + * https://code.google.com/p/google-authenticator/wiki/KeyUriFormat + * Should be done in a better fashion + * + * @param string $type totp or hotp + * @param string $label Label to display this as to the user + * @param string $secret Base32 encoded secret + * @param integer $counter Required by hotp, otherwise ignored + * @param array $options Optional fields that will be set if present + * + * @return string Key URI + */ + public static function getKeyUri($type, $label, $secret, $counter = null, $options = array()) + { + // two types only.. + if (!in_array($type, self::$allowedTypes)) { + throw new \InvalidArgumentException('Type has to be of allowed types list'); + } + + // Label can't be empty + $label = trim($label); + + if (strlen($label) < 1) { + throw new \InvalidArgumentException('Label has to be one or more printable characters'); + } + + if (substr_count($label, ':') > 2) { + throw new \InvalidArgumentException('Account name contains illegal colon characters'); + } + + // Secret needs to be here + if (strlen($secret) < 1) { + throw new \InvalidArgumentException('No secret present'); + } + + // check for counter on hotp + if ($type == 'hotp' && is_null($counter)) { + throw new \InvalidArgumentException('Counter required for hotp'); + } + + // This is the base, these are at least required + $otpauth = 'otpauth://' . $type . '/' . str_replace(array(':', ' '), array('%3A', '%20'), $label) . '?secret=' . rawurlencode($secret); + + if ($type == 'hotp' && !is_null($counter)) { + $otpauth .= '&counter=' . intval($counter); + } + + // Now check the options array + + // algorithm (currently ignored by Authenticator) + // Defaults to SHA1 + if (array_key_exists('algorithm', $options)) { + $otpauth .= '&algorithm=' . rawurlencode($options['algorithm']); + } + + // digits (currently ignored by Authenticator) + // Defaults to 6 + if (array_key_exists('digits', $options) && intval($options['digits']) !== 6 && intval($options['digits']) !== 8) { + throw new \InvalidArgumentException('Digits can only have the values 6 or 8, ' . $options['digits'] . ' given'); + } elseif (array_key_exists('digits', $options)) { + $otpauth .= '&digits=' . intval($options['digits']); + } + + // period, only for totp (currently ignored by Authenticator) + // Defaults to 30 + if ($type == 'totp' && array_key_exists('period', $options)) { + $otpauth .= '&period=' . rawurlencode($options['period']); + } + + // issuer + // Defaults to none + if (array_key_exists('issuer', $options)) { + $otpauth .= '&issuer=' . rawurlencode($options['issuer']); + } + + return $otpauth; + } + + + /** + * Returns the QR code url + * + * Format of encoded url is here: + * https://code.google.com/p/google-authenticator/wiki/KeyUriFormat + * Should be done in a better fashion + * + * @param string $type totp or hotp + * @param string $label Label to display this as to the user + * @param string $secret Base32 encoded secret + * @param integer $counter Required by hotp, otherwise ignored + * @param array $options Optional fields that will be set if present + * + * @return string URL to the QR code + */ + public static function getQrCodeUrl($type, $label, $secret, $counter = null, $options = array()) + { + // Width and height can be overwritten + $width = self::$width; + + if (array_key_exists('width', $options) && is_numeric($options['width'])) { + $width = $options['width']; + } + + $height = self::$height; + + if (array_key_exists('height', $options) && is_numeric($options['height'])) { + $height = $options['height']; + } + + $otpauth = self::getKeyUri($type, $label, $secret, $counter, $options); + + $url = 'https://chart.googleapis.com/chart?chs=' . $width . 'x' + . $height . '&cht=qr&chld=M|0&chl=' . urlencode($otpauth); + + return $url; + } + + /** + * Creates a pseudo random Base32 string + * + * This could decode into anything. It's located here as a small helper + * where code that might need base32 usually also needs something like this. + * + * @param integer $length Exact length of output string + * @return string Base32 encoded random + */ + public static function generateRandom($length = 16) + { + $keys = array_merge(range('A','Z'), range(2,7)); // No padding char + + $string = ''; + + for ($i = 0; $i < $length; $i++) { + $string .= $keys[self::getRand()]; + } + + return $string; + } + + /** + * Get random number + * + * @return int Random number between 0 and 31 (including) + */ + private static function getRand() + { + if (function_exists('random_int')) { + // Uses either the PHP7 internal function or the polyfill if present + return random_int(0, 31); + } elseif (function_exists('openssl_random_pseudo_bytes')) { + $bytes = openssl_random_pseudo_bytes(2); + $number = hexdec(bin2hex($bytes)); + + if ($number > 31) { + $number = $number % 32; + } + + return $number; + } else { + return mt_rand(0, 31); + } + } +} diff --git a/vendor/christian-riesen/otp/src/Otp/Otp.php b/vendor/christian-riesen/otp/src/Otp/Otp.php new file mode 100644 index 00000000..7d954870 --- /dev/null +++ b/vendor/christian-riesen/otp/src/Otp/Otp.php @@ -0,0 +1,310 @@ +<?php +namespace Otp; + +/** + * One Time Passwords + * + * Last update: 2012-06-16 + * + * Implements HOTP and TOTP + * + * HMAC-Based One-time Password(HOTP) algorithm specified in RFC 4226 + * @link https://tools.ietf.org/html/rfc4226 + * + * Time-based One-time Password (TOTP) algorithm specified in RFC 6238 + * @link https://tools.ietf.org/html/rfc6238 + * + * As a note: This code is NOT 2038 proof! The min concern is the function + * getBinaryCounter that uses the pack function which can't handle 64bit yet. + * + * Can be easy used with Google Authenticator + * @link https://code.google.com/p/google-authenticator/ + * + * @author Christian Riesen <chris.riesen@gmail.com> + * @link http://christianriesen.com + * @license MIT License see LICENSE file + */ + +class Otp implements OtpInterface +{ + /** + * The digits the code can have + * + * Either 6 or 8. + * Authenticator does only support 6. + * + * @var integer + */ + protected $digits = 6; + + /** + * Time in seconds one counter period is long + * + * @var integer + */ + protected $period = 30; + + /** + * Possible algorithms + * + * @var array + */ + protected $allowedAlgorithms = array('sha1', 'sha256', 'sha512'); + + /** + * Currently used algorithm + * + * @var string + */ + protected $algorithm = 'sha1'; + + /* (non-PHPdoc) + * @see Otp.OtpInterface::hotp() + */ + public function hotp($secret, $counter) + { + if (!is_numeric($counter)) { + throw new \InvalidArgumentException('Counter must be integer'); + } + + $hash = hash_hmac( + $this->algorithm, + $this->getBinaryCounter($counter), + $secret, + true + ); + + return str_pad($this->truncate($hash), $this->digits, '0', STR_PAD_LEFT); + } + + /* (non-PHPdoc) + * @see Otp.OtpInterface::totp() + */ + public function totp($secret, $timecounter = null) + { + if (is_null($timecounter)) { + $timecounter = $this->getTimecounter(); + } + + return $this->hotp($secret, $timecounter); + } + + /* (non-PHPdoc) + * @see Otp.OtpInterface::checkHotp() + */ + public function checkHotp($secret, $counter, $key) + { + return $this->safeCompare($this->hotp($secret, $counter), $key); + } + + /* (non-PHPdoc) + * @see Otp.OtpInterface::checkTotp() + */ + public function checkTotp($secret, $key, $timedrift = 1) + { + if (!is_numeric($timedrift) || $timedrift < 0) { + throw new \InvalidArgumentException('Invalid timedrift supplied'); + } + // Counter comes from time now + // Also we check the current timestamp as well as previous and future ones + // according to $timerange + $timecounter = $this->getTimecounter(); + + $start = $timecounter - ($timedrift); + $end = $timecounter + ($timedrift); + + // We first try the current, as it is the most likely to work + if ($this->safeCompare($this->totp($secret, $timecounter), $key)) { + return true; + } elseif ($timedrift == 0) { + // When timedrift is 0, this is the end of the checks + return false; + } + + // Well, that didn't work, so try the others + for ($t = $start; $t <= $end; $t = $t + 1) { + if ($t == $timecounter) { + // Already tried that one + continue; + } + + if ($this->safeCompare($this->totp($secret, $t), $key)) { + return true; + } + } + + // if none worked, then return false + return false; + } + + /** + * Changing the used algorithm for hashing + * + * Can only be one of the algorithms in the allowedAlgorithms property. + * + * @param string $algorithm + * @throws \InvalidArgumentException + * @return \Otp\Otp + */ + + /* + * This has been disabled since it does not bring the expected results + * according to the RFC test vectors for sha256 or sha512. + * Until that is fixed, the algorithm simply stays at sha1. + * Google Authenticator does not support sha256 and sha512 at the moment. + * + + public function setAlgorithm($algorithm) + { + if (!in_array($algorithm, $this->allowedAlgorithms)) { + throw new \InvalidArgumentException('Not an allowed algorithm: ' . $algorithm); + } + + $this->algorithm = $algorithm; + + return $this; + } + // */ + + /** + * Get the algorithms name (lowercase) + * + * @return string + */ + public function getAlgorithm() + { + return $this->algorithm; + } + + /** + * Setting period lenght for totp + * + * @param integer $period + * @throws \InvalidArgumentException + * @return \Otp\Otp + */ + public function setPeriod($period) + { + if (!is_int($period)) { + throw new \InvalidArgumentException('Period must be an integer'); + } + + $this->period = $period; + + return $this; + } + + /** + * Returns the set period value + * + * @return integer + */ + public function getPeriod() + { + return $this->period; + } + + /** + * Setting number of otp digits + * + * @param integer $digits Number of digits for the otp (6 or 8) + * @throws \InvalidArgumentException + * @return \Otp\Otp + */ + public function setDigits($digits) + { + if (!in_array($digits, array(6, 8))) { + throw new \InvalidArgumentException('Digits must be 6 or 8'); + } + + $this->digits = $digits; + + return $this; + } + + /** + * Returns number of digits in the otp + * + * @return integer + */ + public function getDigits() + { + return $this->digits; + } + + /** + * Generates a binary counter for hashing + * + * Warning: Not 2038 safe. Maybe until then pack supports 64bit. + * + * @param integer $counter Counter in integer form + * @return string Binary string + */ + protected function getBinaryCounter($counter) + { + return pack('N*', 0) . pack('N*', $counter); + } + + /** + * Generating time counter + * + * This is the time divided by 30 by default. + * + * @return integer Time counter + */ + protected function getTimecounter() + { + return floor(time() / $this->period); + } + + /** + * Creates the basic number for otp from hash + * + * This number is left padded with zeros to the required length by the + * calling function. + * + * @param string $hash hmac hash + * @return number + */ + protected function truncate($hash) + { + $offset = ord($hash[19]) & 0xf; + + return ( + ((ord($hash[$offset+0]) & 0x7f) << 24 ) | + ((ord($hash[$offset+1]) & 0xff) << 16 ) | + ((ord($hash[$offset+2]) & 0xff) << 8 ) | + (ord($hash[$offset+3]) & 0xff) + ) % pow(10, $this->digits); + } + + /** + * Safely compares two inputs + * + * Assumed inputs are numbers and strings. + * Compares them in a time linear manner. No matter how much you guess + * correct of the partial content, it does not change the time it takes to + * run the entire comparison. + * + * @param mixed $a + * @param mixed $b + * @return boolean + */ + protected function safeCompare($a, $b) + { + $sha1a = sha1($a); + $sha1b = sha1($b); + + // Now the compare is always the same length. Even considering minute + // time differences in sha1 creation, all you know is that a longer + // input takes longer to hash, not how long the actual compared value is + $result = 0; + + for ($i = 0; $i < 40; $i++) { + $result |= ord($sha1a[$i]) ^ ord($sha1b[$i]); + } + + return $result == 0; + } +} + diff --git a/vendor/christian-riesen/otp/src/Otp/OtpInterface.php b/vendor/christian-riesen/otp/src/Otp/OtpInterface.php new file mode 100644 index 00000000..7ff34f2f --- /dev/null +++ b/vendor/christian-riesen/otp/src/Otp/OtpInterface.php @@ -0,0 +1,65 @@ +<?php +namespace Otp; + +/** + * Interface for HOTP and TOTP + * + * Last update: 2012-06-16 + * + * HMAC-Based One-time Password(HOTP) algorithm specified in RFC 4226 + * @link https://tools.ietf.org/html/rfc4226 + * + * Time-based One-time Password (TOTP) algorithm specified in RFC 6238 + * @link https://tools.ietf.org/html/rfc6238 + * + * @author Christian Riesen <chris.riesen@gmail.com> + * @link http://christianriesen.com + * @license MIT License see LICENSE file + */ + +interface OtpInterface +{ + /** + * Returns OTP using the HOTP algorithm + * + * @param string $secret + * @param integer $counter + * @return string One Time Password + */ + function hotp($secret, $counter); + + /** + * Returns OTP using the TOTP algorithm + * + * @param string $secret + * @param integer $timecounter Optional: Uses current time if null + * @return string One Time Password + */ + function totp($secret, $timecounter = null); + + /** + * Checks Hotp against a key + * + * This is a helper function, but is here to ensure the Totp can be checked + * in the same manner. + * + * @param string $secret + * @param integer $counter + * @param string $key + * + * @return boolean If key is correct + */ + function checkHotp($secret, $counter, $key); + + /** + * Checks Totp agains a key + * + * + * @param string $secret + * @param integer $key + * @param integer $timedrift + * + * @return boolean If key is correct + */ + function checkTotp($secret, $key, $timedrift = 1); +} diff --git a/vendor/christian-riesen/otp/tests/Otp/GoogleAuthenticatorTest.php b/vendor/christian-riesen/otp/tests/Otp/GoogleAuthenticatorTest.php new file mode 100644 index 00000000..219bdaa8 --- /dev/null +++ b/vendor/christian-riesen/otp/tests/Otp/GoogleAuthenticatorTest.php @@ -0,0 +1,88 @@ +<?php + +require_once __DIR__ . '/../../src/Otp/GoogleAuthenticator.php'; + +use Otp\GoogleAuthenticator; + +/** + * GoogleAuthenticator test case. + */ +class GoogleAuthenticatorTest extends \PHPUnit_Framework_TestCase +{ + /** + * Tests getQrCodeUrl + */ + public function testGetQrCodeUrl() + { + $secret = 'MEP3EYVA6XNFNVNM'; // testing secret + + // Standard totp case + $this->assertEquals( + 'https://chart.googleapis.com/chart?chs=200x200&cht=qr&chld=M|0&chl=otpauth%3A%2F%2Ftotp%2Fuser%40host.com%3Fsecret%3DMEP3EYVA6XNFNVNM', + GoogleAuthenticator::getQrCodeUrl('totp', 'user@host.com', $secret) + ); + + // hotp (include a counter) + $this->assertEquals( + 'https://chart.googleapis.com/chart?chs=200x200&cht=qr&chld=M|0&chl=otpauth%3A%2F%2Fhotp%2Fuser%40host.com%3Fsecret%3DMEP3EYVA6XNFNVNM%26counter%3D1234', + GoogleAuthenticator::getQrCodeUrl('hotp', 'user@host.com', $secret, 1234) + ); + + // totp, this time with a parameter for chaning the size of the QR + $this->assertEquals( + 'https://chart.googleapis.com/chart?chs=300x300&cht=qr&chld=M|0&chl=otpauth%3A%2F%2Ftotp%2Fuser%40host.com%3Fsecret%3DMEP3EYVA6XNFNVNM', + GoogleAuthenticator::getQrCodeUrl('totp', 'user@host.com', $secret, null, array('height' => 300, 'width' => 300)) + ); + + } + + /** + * Tests getKeyUri + */ + public function testGetKeyUri() + { + $secret = 'MEP3EYVA6XNFNVNM'; // testing secret + + // Standard totp case + $this->assertEquals( + 'otpauth://totp/user@host.com?secret=MEP3EYVA6XNFNVNM', + GoogleAuthenticator::getKeyUri('totp', 'user@host.com', $secret) + ); + + // hotp (include a counter) + $this->assertEquals( + 'otpauth://hotp/user@host.com?secret=MEP3EYVA6XNFNVNM&counter=1234', + GoogleAuthenticator::getKeyUri('hotp', 'user@host.com', $secret, 1234) + ); + + // totp/hotp with an issuer in the label + $this->assertEquals( + 'otpauth://hotp/issuer%3Auser@host.com?secret=MEP3EYVA6XNFNVNM&counter=1234', + GoogleAuthenticator::getKeyUri('hotp', 'issuer:user@host.com', $secret, 1234) + ); + + // totp/hotp with an issuer and spaces in the label + $this->assertEquals( + 'otpauth://hotp/an%20issuer%3A%20user@host.com?secret=MEP3EYVA6XNFNVNM&counter=1234', + GoogleAuthenticator::getKeyUri('hotp', 'an issuer: user@host.com', $secret, 1234) + ); + + // totp/hotp with an issuer as option + $this->assertEquals( + 'otpauth://hotp/an%20issuer%3Auser@host.com?secret=MEP3EYVA6XNFNVNM&counter=1234&issuer=an%20issuer', + GoogleAuthenticator::getKeyUri('hotp', 'an issuer:user@host.com', $secret, 1234, array('issuer' => 'an issuer')) + ); + } + + /** + * Tests generateRandom + */ + public function testGenerateRandom() + { + // contains numbers 2-7 and letters A-Z in large letters, 16 chars long + $this->assertRegExp('/[2-7A-Z]{16}/', GoogleAuthenticator::generateRandom()); + + // Can be told to make a longer secret + $this->assertRegExp('/[2-7A-Z]{18}/', GoogleAuthenticator::generateRandom(18)); + } +} diff --git a/vendor/christian-riesen/otp/tests/Otp/OtpTest.php b/vendor/christian-riesen/otp/tests/Otp/OtpTest.php new file mode 100644 index 00000000..ccfdddab --- /dev/null +++ b/vendor/christian-riesen/otp/tests/Otp/OtpTest.php @@ -0,0 +1,124 @@ +<?php + +require_once __DIR__ . '/../../src/Otp/OtpInterface.php'; +require_once __DIR__ . '/../../src/Otp/Otp.php'; + +use Otp\Otp; + +/** + * Otp test case. + */ +class OtpTest extends \PHPUnit_Framework_TestCase +{ + /** + * + * @var Otp + */ + private $Otp; + + private $secret = "12345678901234567890"; + + /** + * Prepares the environment before running a test. + */ + protected function setUp() + { + parent::setUp(); + + $this->Otp = new Otp(); + + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->Otp = null; + + parent::tearDown(); + } + + /** + * Tests Otp->hotp() + * + * Using test vectors from RFC + * https://tools.ietf.org/html/rfc4226 + */ + public function testHotpRfc() + { + $secret = $this->secret; + + $this->assertEquals('755224', $this->Otp->hotp($secret, 0)); + $this->assertEquals('287082', $this->Otp->hotp($secret, 1)); + $this->assertEquals('359152', $this->Otp->hotp($secret, 2)); + $this->assertEquals('969429', $this->Otp->hotp($secret, 3)); + $this->assertEquals('338314', $this->Otp->hotp($secret, 4)); + $this->assertEquals('254676', $this->Otp->hotp($secret, 5)); + $this->assertEquals('287922', $this->Otp->hotp($secret, 6)); + $this->assertEquals('162583', $this->Otp->hotp($secret, 7)); + $this->assertEquals('399871', $this->Otp->hotp($secret, 8)); + $this->assertEquals('520489', $this->Otp->hotp($secret, 9)); + } + + /** + * Tests TOTP general construction + * + * Still uses the hotp function, but since totp is a bit more special, has + * its own tests + * Using test vectors from RFC + * https://tools.ietf.org/html/rfc6238 + */ + public function testTotpRfc() + { + $secret = $this->secret; + + // Test vectors are in 8 digits + $this->Otp->setDigits(8); + + // The time presented in the test vector has to be first divided through 30 + // to count as the key + + // SHA 1 grouping + $this->assertEquals('94287082', $this->Otp->hotp($secret, floor(59/30)), 'sha1 with time 59'); + $this->assertEquals('07081804', $this->Otp->hotp($secret, floor(1111111109/30)), 'sha1 with time 1111111109'); + $this->assertEquals('14050471', $this->Otp->hotp($secret, floor(1111111111/30)), 'sha1 with time 1111111111'); + $this->assertEquals('89005924', $this->Otp->hotp($secret, floor(1234567890/30)), 'sha1 with time 1234567890'); + $this->assertEquals('69279037', $this->Otp->hotp($secret, floor(2000000000/30)), 'sha1 with time 2000000000'); + $this->assertEquals('65353130', $this->Otp->hotp($secret, floor(20000000000/30)), 'sha1 with time 20000000000'); + + /* + The following tests do NOT pass. + Once the otp class can deal with these correctly, they can be used again. + They are here for completeness test vectors from the RFC. + + // SHA 256 grouping + $this->Otp->setAlgorithm('sha256'); + $this->assertEquals('46119246', $this->Otp->hotp($secret, floor(59/30)), 'sha256 with time 59'); + $this->assertEquals('07081804', $this->Otp->hotp($secret, floor(1111111109/30)), 'sha256 with time 1111111109'); + $this->assertEquals('14050471', $this->Otp->hotp($secret, floor(1111111111/30)), 'sha256 with time 1111111111'); + $this->assertEquals('89005924', $this->Otp->hotp($secret, floor(1234567890/30)), 'sha256 with time 1234567890'); + $this->assertEquals('69279037', $this->Otp->hotp($secret, floor(2000000000/30)), 'sha256 with time 2000000000'); + $this->assertEquals('65353130', $this->Otp->hotp($secret, floor(20000000000/30)), 'sha256 with time 20000000000'); + + // SHA 512 grouping + $this->Otp->setAlgorithm('sha512'); + $this->assertEquals('90693936', $this->Otp->hotp($secret, floor(59/30)), 'sha512 with time 59'); + $this->assertEquals('25091201', $this->Otp->hotp($secret, floor(1111111109/30)), 'sha512 with time 1111111109'); + $this->assertEquals('99943326', $this->Otp->hotp($secret, floor(1111111111/30)), 'sha512 with time 1111111111'); + $this->assertEquals('93441116', $this->Otp->hotp($secret, floor(1234567890/30)), 'sha512 with time 1234567890'); + $this->assertEquals('38618901', $this->Otp->hotp($secret, floor(2000000000/30)), 'sha512 with time 2000000000'); + $this->assertEquals('47863826', $this->Otp->hotp($secret, floor(20000000000/30)), 'sha512 with time 20000000000'); + */ + } + + /** + * @expectedException InvalidArgumentException + * @expectedExceptionMessage Counter must be integer + */ + public function testHotpInvalidCounter() + { + $this->Otp->hotp($this->secret, 'a'); + } + +} |