TYPO3 API  SVNRelease
tx_saltedpasswords_salts_blowfishTest.php
Go to the documentation of this file.
00001 <?php
00002 /***************************************************************
00003 *  Copyright notice
00004 *
00005 *  (c) 2009-2011 Marcus Krause <marcus#exp2009@t3sec.info>
00006 *  All rights reserved
00007 *
00008 *  This script is part of the TYPO3 project. The TYPO3 project is
00009 *  free software; you can redistribute it and/or modify
00010 *  it under the terms of the GNU General Public License as published by
00011 *  the Free Software Foundation; either version 2 of the License, or
00012 *  (at your option) any later version.
00013 *
00014 *  The GNU General Public License can be found at
00015 *  http://www.gnu.org/copyleft/gpl.html.
00016 *  A copy is found in the textfile GPL.txt and important notices to the license
00017 *  from the author is found in LICENSE.txt distributed with these scripts.
00018 *
00019 *
00020 *  This script is distributed in the hope that it will be useful,
00021 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00022 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00023 *  GNU General Public License for more details.
00024 *
00025 *  This copyright notice MUST APPEAR in all copies of the script!
00026 ***************************************************************/
00027 /**
00028  * Contains class "tx_saltedpasswords_salts_blowfish"
00029  * that provides Blowfish salted hashing.
00030  *
00031  * $Id: tx_saltedpasswords_salts_blowfishTest.php 10120 2011-01-18 20:03:36Z ohader $
00032  */
00033 
00034 /**
00035  * Testcases for class tx_saltedpasswords_salts_blowfish.
00036  *
00037  * @author  Marcus Krause <marcus#exp2009@t3sec.info>
00038  * @package  TYPO3
00039  * @subpackage  tx_saltedpasswords
00040  */
00041 class tx_saltedpasswords_salts_blowfishTest extends tx_phpunit_testcase {
00042 
00043 
00044     /**
00045      * Keeps instance of object to test.
00046      *
00047      * @var tx_saltedpasswords_salts_blowfish
00048      */
00049     protected $objectInstance = NULL;
00050 
00051 
00052     /**
00053      * Sets up the fixtures for this testcase.
00054      *
00055      * @return  void
00056      */
00057     public function setUp() {
00058         $this->objectInstance = t3lib_div::makeInstance('tx_saltedpasswords_salts_blowfish');
00059     }
00060 
00061     /**
00062      * Tears down objects and settings created in this testcase.
00063      *
00064      * @return  void
00065      */
00066     public function tearDown() {
00067         unset($this->objectInstance);
00068     }
00069 
00070     /**
00071      * Marks tests as skipped if the blowfish method is not available.
00072      *
00073      * @return  void
00074      */
00075     protected function skipTestIfBlowfishIsNotAvailable() {
00076         if (!CRYPT_BLOWFISH) {
00077             $this->markTestSkipped('Blowfish is not supported on your platform.');
00078         }
00079     }
00080 
00081     /**
00082      * @test
00083      */
00084     public function hasCorrectBaseClass() {
00085 
00086         $hasCorrectBaseClass = (0 === strcmp('tx_saltedpasswords_salts_blowfish', get_class($this->objectInstance))) ? TRUE : FALSE;
00087 
00088             // XCLASS ?
00089         if (!$hasCorrectBaseClass && FALSE != get_parent_class($this->objectInstance)) {
00090             $hasCorrectBaseClass = is_subclass_of($this->objectInstance, 'tx_saltedpasswords_salts_blowfish');
00091         }
00092 
00093         $this->assertTrue($hasCorrectBaseClass);
00094     }
00095 
00096     /**
00097      * @test
00098      */
00099     public function nonZeroSaltLength() {
00100         $this->assertTrue($this->objectInstance->getSaltLength() > 0);
00101     }
00102 
00103     /**
00104      * @test
00105      */
00106     public function emptyPasswordResultsInNullSaltedPassword() {
00107         $password = '';
00108         $this->assertNull($this->objectInstance->getHashedPassword($password));
00109     }
00110 
00111     /**
00112      * @test
00113      */
00114     public function nonEmptyPasswordResultsInNonNullSaltedPassword() {
00115         $this->skipTestIfBlowfishIsNotAvailable();
00116 
00117         $password = 'a';
00118         $this->assertNotNull($this->objectInstance->getHashedPassword($password));
00119     }
00120 
00121     /**
00122      * @test
00123      */
00124     public function createdSaltedHashOfProperStructure() {
00125         $this->skipTestIfBlowfishIsNotAvailable();
00126 
00127         $password = 'password';
00128         $saltedHashPassword = $this->objectInstance->getHashedPassword($password);
00129         $this->assertTrue($this->objectInstance->isValidSaltedPW($saltedHashPassword));
00130     }
00131 
00132     /**
00133      * @test
00134      */
00135     public function createdSaltedHashOfProperStructureForCustomSaltWithoutSetting() {
00136         $this->skipTestIfBlowfishIsNotAvailable();
00137 
00138         $password = 'password';
00139 
00140             // custom salt without setting
00141         $randomBytes = t3lib_div::generateRandomBytes($this->objectInstance->getSaltLength());
00142         $salt = $this->objectInstance->base64Encode($randomBytes, $this->objectInstance->getSaltLength());
00143         $this->assertTrue($this->objectInstance->isValidSalt($salt));
00144 
00145         $saltedHashPassword = $this->objectInstance->getHashedPassword($password, $salt);
00146         $this->assertTrue($this->objectInstance->isValidSaltedPW($saltedHashPassword));
00147     }
00148 
00149     /**
00150      * @test
00151      */
00152     public function createdSaltedHashOfProperStructureForMaximumHashCount() {
00153         $this->skipTestIfBlowfishIsNotAvailable();
00154 
00155         $password = 'password';
00156         $maxHashCount = $this->objectInstance->getMaxHashCount();
00157         $this->objectInstance->setHashCount($maxHashCount);
00158         $saltedHashPassword = $this->objectInstance->getHashedPassword($password);
00159         $this->assertTrue($this->objectInstance->isValidSaltedPW($saltedHashPassword));
00160             // reset hashcount
00161         $this->objectInstance->setHashCount(NULL);
00162     }
00163 
00164     /**
00165      * @test
00166      */
00167     public function createdSaltedHashOfProperStructureForMinimumHashCount() {
00168         $this->skipTestIfBlowfishIsNotAvailable();
00169 
00170         $password = 'password';
00171         $minHashCount = $this->objectInstance->getMinHashCount();
00172         $this->objectInstance->setHashCount($minHashCount);
00173         $saltedHashPassword = $this->objectInstance->getHashedPassword($password);
00174         $this->assertTrue($this->objectInstance->isValidSaltedPW($saltedHashPassword));
00175             // reset hashcount
00176         $this->objectInstance->setHashCount(NULL);
00177     }
00178 
00179     /**
00180      * Tests authentication procedure with alphabet characters.
00181      *
00182      * Checks if a "plain-text password" is everytime mapped to the
00183      * same "salted password hash" when using the same salt.
00184      *
00185      * @test
00186      */
00187     public function authenticationWithValidAlphaCharClassPassword() {
00188         $this->skipTestIfBlowfishIsNotAvailable();
00189 
00190         $password = 'aEjOtY';
00191 
00192         $saltedHashPassword = $this->objectInstance->getHashedPassword($password);
00193         $this->assertTrue($this->objectInstance->checkPassword($password, $saltedHashPassword));
00194     }
00195 
00196     /**
00197      * Tests authentication procedure with numeric characters.
00198      *
00199      * Checks if a "plain-text password" is everytime mapped to the
00200      * same "salted password hash" when using the same salt.
00201      *
00202      * @test
00203      */
00204     public function authenticationWithValidNumericCharClassPassword() {
00205         $this->skipTestIfBlowfishIsNotAvailable();
00206 
00207         $password = '01369';
00208 
00209         $saltedHashPassword = $this->objectInstance->getHashedPassword($password);
00210         $this->assertTrue($this->objectInstance->checkPassword($password, $saltedHashPassword));
00211     }
00212 
00213     /**
00214      * Tests authentication procedure with US-ASCII special characters.
00215      *
00216      * Checks if a "plain-text password" is everytime mapped to the
00217      * same "salted password hash" when using the same salt.
00218      *
00219      * @test
00220      */
00221     public function authenticationWithValidAsciiSpecialCharClassPassword() {
00222         $this->skipTestIfBlowfishIsNotAvailable();
00223 
00224         $password = ' !"#$%&\'()*+,-./:;<=>?@[\]^_`{|}~';
00225 
00226         $saltedHashPassword = $this->objectInstance->getHashedPassword($password);
00227         $this->assertTrue($this->objectInstance->checkPassword($password, $saltedHashPassword));
00228     }
00229 
00230     /**
00231      * Tests authentication procedure with latin1 special characters.
00232      *
00233      * Checks if a "plain-text password" is everytime mapped to the
00234      * same "salted password hash" when using the same salt.
00235      *
00236      * @test
00237      */
00238     public function authenticationWithValidLatin1SpecialCharClassPassword() {
00239         $this->skipTestIfBlowfishIsNotAvailable();
00240 
00241         $password = '';
00242         for ($i = 160; $i <= 191; $i++) {
00243             $password .= chr($i);
00244         }
00245         $password .= chr(215) . chr(247);
00246 
00247         $saltedHashPassword = $this->objectInstance->getHashedPassword($password);
00248         $this->assertTrue($this->objectInstance->checkPassword($password, $saltedHashPassword));
00249     }
00250 
00251     /**
00252      * Tests authentication procedure with latin1 umlauts.
00253      *
00254      * Checks if a "plain-text password" is everytime mapped to the
00255      * same "salted password hash" when using the same salt.
00256      *
00257      * @test
00258      */
00259     public function authenticationWithValidLatin1UmlautCharClassPassword() {
00260         $this->skipTestIfBlowfishIsNotAvailable();
00261 
00262         $password = '';
00263         for ($i = 192; $i <= 214; $i++) {
00264             $password .= chr($i);
00265         }
00266         for ($i = 216; $i <= 246; $i++) {
00267             $password .= chr($i);
00268         }
00269         for ($i = 248; $i <= 255; $i++) {
00270             $password .= chr($i);
00271         }
00272 
00273         $saltedHashPassword = $this->objectInstance->getHashedPassword($password);
00274         $this->assertTrue($this->objectInstance->checkPassword($password, $saltedHashPassword));
00275     }
00276 
00277     /**
00278      * @test
00279      */
00280     public function authenticationWithNonValidPassword() {
00281         $this->skipTestIfBlowfishIsNotAvailable();
00282 
00283         $password = 'password';
00284         $password1 = $password . 'INVALID';
00285         $saltedHashPassword = $this->objectInstance->getHashedPassword($password);
00286         $this->assertFalse($this->objectInstance->checkPassword($password1, $saltedHashPassword));
00287     }
00288 
00289     /**
00290      * @test
00291      */
00292     public function passwordVariationsResultInDifferentHashes() {
00293         $this->skipTestIfBlowfishIsNotAvailable();
00294 
00295         $pad = 'a';
00296         $password = '';
00297         $criticalPwLength = 0;
00298             // We're using a constant salt.
00299         $saltedHashPasswordPrevious = $saltedHashPasswordCurrent = $salt = $this->objectInstance->getHashedPassword($pad);
00300 
00301         for ($i = 0; $i <= 128; $i += 8) {
00302             $password = str_repeat($pad, max($i, 1));
00303             $saltedHashPasswordPrevious = $saltedHashPasswordCurrent;
00304             $saltedHashPasswordCurrent = $this->objectInstance->getHashedPassword($password, $salt);
00305             if ($i > 0 && 0 == strcmp($saltedHashPasswordPrevious, $saltedHashPasswordCurrent)) {
00306                 $criticalPwLength = $i;
00307                 break;
00308             }
00309         }
00310         $this->assertTrue(($criticalPwLength == 0) || ($criticalPwLength > 32), 'Duplicates of hashed passwords with plaintext password of length ' . $criticalPwLength . '+.');
00311     }
00312 
00313     /**
00314      * @test
00315      */
00316     public function modifiedMinHashCount() {
00317         $minHashCount = $this->objectInstance->getMinHashCount();
00318         $this->objectInstance->setMinHashCount($minHashCount - 1);
00319         $this->assertTrue($this->objectInstance->getMinHashCount() < $minHashCount);
00320         $this->objectInstance->setMinHashCount($minHashCount + 1);
00321         $this->assertTrue($this->objectInstance->getMinHashCount() > $minHashCount);
00322     }
00323 
00324     /**
00325      * @test
00326      */
00327     public function modifiedMaxHashCount() {
00328         $maxHashCount = $this->objectInstance->getMaxHashCount();
00329         $this->objectInstance->setMaxHashCount($maxHashCount + 1);
00330         $this->assertTrue($this->objectInstance->getMaxHashCount() > $maxHashCount);
00331         $this->objectInstance->setMaxHashCount($maxHashCount - 1);
00332         $this->assertTrue($this->objectInstance->getMaxHashCount() < $maxHashCount);
00333     }
00334 
00335     /**
00336      * @test
00337      */
00338     public function modifiedHashCount() {
00339         $hashCount = $this->objectInstance->getHashCount();
00340         $this->objectInstance->setMaxHashCount($hashCount + 1);
00341         $this->objectInstance->setHashCount($hashCount + 1);
00342         $this->assertTrue($this->objectInstance->getHashCount() > $hashCount);
00343         $this->objectInstance->setMinHashCount($hashCount - 1);
00344         $this->objectInstance->setHashCount($hashCount - 1);
00345         $this->assertTrue($this->objectInstance->getHashCount() < $hashCount);
00346             // reset hashcount
00347         $this->objectInstance->setHashCount(NULL);
00348     }
00349 
00350     /**
00351      * @test
00352      */
00353     public function updateNecessityForValidSaltedPassword() {
00354         $this->skipTestIfBlowfishIsNotAvailable();
00355 
00356         $password = 'password';
00357         $saltedHashPassword = $this->objectInstance->getHashedPassword($password);
00358         $this->assertFalse($this->objectInstance->isHashUpdateNeeded($saltedHashPassword));
00359     }
00360 
00361     /**
00362      * @test
00363      */
00364     public function updateNecessityForIncreasedHashcount() {
00365         $password = 'password';
00366         $saltedHashPassword = $this->objectInstance->getHashedPassword($password);
00367         $increasedHashCount = $this->objectInstance->getHashCount() + 1;
00368         $this->objectInstance->setMaxHashCount($increasedHashCount);
00369         $this->objectInstance->setHashCount($increasedHashCount);
00370         $this->assertTrue($this->objectInstance->isHashUpdateNeeded($saltedHashPassword));
00371             // reset hashcount
00372         $this->objectInstance->setHashCount(NULL);
00373     }
00374 
00375     /**
00376      * @test
00377      */
00378     public function updateNecessityForDecreasedHashcount() {
00379         $this->skipTestIfBlowfishIsNotAvailable();
00380 
00381         $password = 'password';
00382         $saltedHashPassword = $this->objectInstance->getHashedPassword($password);
00383         $decreasedHashCount = $this->objectInstance->getHashCount() - 1;
00384         $this->objectInstance->setMinHashCount($decreasedHashCount);
00385         $this->objectInstance->setHashCount($decreasedHashCount);
00386         $this->assertFalse($this->objectInstance->isHashUpdateNeeded($saltedHashPassword));
00387             // reset hashcount
00388         $this->objectInstance->setHashCount(NULL);
00389     }
00390 }
00391 ?>