Subversion Repositories php_utils

Rev

Rev 2 | Go to most recent revision | Blame | Last modification | View Log | RSS feed

  1. <?php
  2.  
  3. // Mastercopy of this file:
  4. // http://www.viathinksoft.de/~daniel-marschall/code/php/marschallHash.phps
  5.  
  6. function MHA($password, $iteratedSalt='', $iterations=1987, $binary_output=false) {
  7.  
  8.         // --------------------------------------------------------------------------------
  9.         // MarschallHash: Uncrackable hash with multiple SHA1 iterations in base64 encoding
  10.         // This function is pretty slow because of the iterations, but therefore secure
  11.         // against offline attacks or rainbowtables. Also, the slowlyness of this hash
  12.         // makes the creation of rainbow tables much harder.
  13.         //
  14.         // (C)Copyright 2011 Daniel Marschall, ViaThinkSoft. All rights reserved.
  15.         // www.daniel-marschall.de / www.viathinksoft.de
  16.         //
  17.         // Notation of the hash name:
  18.         // - MHA-1987 (resp. MHA-xxxx where xxxx stands for $iterations)
  19.         // - MHAb-1987 is used for the binary output variant
  20.         // - MD5MHA-1987 is used if $password is a (unsalted!) md5-hash
  21.         // - MD5MHAb-1987 is used for the binary output variant.
  22.         //
  23.         // Default parameters:
  24.         // iteratedSalt  = ''       --> you should change this value to something
  25.         //                              user-specific (e.g. username) or something random
  26.         // iterations    = 1987     --> you should ONLY change this value to a lower
  27.         //                              one if you have performance issues
  28.         // binary_output = no       --> base64 encoding is chosen by default
  29.         //
  30.         // Format:
  31.         // - MHA has the same length as SHA1.
  32.         // - MHA in base64 format has a constant length of 27 bytes and is case sensitive.
  33.         // - MHA in binary format has a constant length of 20 bytes.
  34.         //
  35.         // Comparison 1:
  36.         // x         = ''
  37.         // SHA1(x)   = 'da39a3ee5e6b4b0d3255bfef95601890afd80709'                         (Len = 40)
  38.         // SHA256(x) = 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855' (Len = 64)
  39.         // MD5(x)    = 'd41d8cd98f00b204e9800998ecf8427e'                                 (Len = 32)
  40.         // MHA(x)    = 'UOLv7DgK5/4S7994FeSWZkHDJoQ'                                      (Len = 27)
  41.         //
  42.         // Comparison 2:
  43.         // x         = 'The quick brown fox jumps over the lazy dog'
  44.         // SHA1(x)   = '2fd4e1c67a2d28fced849ee1bb76e7391b93eb12'                         (Len = 40)
  45.         // SHA256(x) = 'd7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592' (Len = 64)
  46.         // MD5(x)    = '9e107d9d372bb6826bd81d3542a419d6'                                 (Len = 32)
  47.         // MHA(x)    = 'Bqdd38sigmurBt6kU/0q99GWSnE'                                      (Len = 27)
  48.         //
  49.         // Mechanism:
  50.         // MHA(x, iteratedSalt, iterations) = optionalBase64(rec(iterations, x, iteratedSalt))
  51.         // rec[n, x, iteratedSalt] = binarySHA1(iteratedSalt || rec[n-1, x, iteratedSalt] || iteratedSalt)
  52.         // rec[0, x, iteratedSalt] = x
  53.         //
  54.         // Recommended usage:
  55.         // - Use the username as iteratedSalt
  56.         //      MHA($password, $username) == $database_hash
  57.         // - If you want to upgrade your existing user-database, e.g. already hashed with md5():
  58.         //   a) Update your database:
  59.         //      $database_hash_new = MHA($database_hash_old, $username)
  60.         //   b) Compare with this variant:
  61.         //      MHA(md5($password), $username) == $database_hash_new
  62.         //      or
  63.         //      MD5_MHA($password, $username) == $database_hash_new
  64.         //
  65.         // Revision: 2011-07-21 (fixed typo in 2013-03-09)
  66.         // --------------------------------------------------------------------------------
  67.  
  68.         if ($iterations < 1) {
  69.                 trigger_error('at function ' . __FUNCTION__ . ': $iterations has to be greater or equal 1', E_USER_ERROR);
  70.                 return false;
  71.         }
  72.  
  73.         $m = $password;
  74.  
  75.         for ($i=1; $i<=$iterations; $i++) {
  76.                 $m = sha1($iteratedSalt.$m.$iteratedSalt, true); // SHA1 with binary output
  77.         }
  78.  
  79.         if (!$binary_output) {
  80.                 $m = base64_encode($m);
  81.  
  82.                 // Remove single "=" at the end
  83.                 # $m = str_replace('=', '', $m);
  84.                 $m = substr($m, 0, 27);
  85.         }
  86.  
  87.         return $m;
  88. }
  89.  
  90. // --- The following functions are useable for database migration ---
  91.  
  92. function MD5_TO_MD5_MHA($md5_hash, $iteratedSalt='', $iterations=1987, $binary_output=false) {
  93.         // Use this function to migrate a (unsalted!) md5 hash into a MD5MHA hash
  94.         //
  95.         // Actually, this is just an alias of MHA()
  96.  
  97.         return marschallHash($md5_hash, $iteratedSalt, $iterations, $binary_output);
  98. }
  99.  
  100. function MD5_MHA($password, $iteratedSalt='', $iterations=1987, $binary_output=false) {
  101.         // Use this function if you have a MD5MHA hash instead of a MHA hash
  102.         //
  103.         // MD5MHA() is equal to MHA(MD5()) where MD5() is unsalted!
  104.  
  105.         return MHA(md5($password), $iteratedSalt, $iterations, $binary_output);
  106. }
  107.  
  108. function MD5MHA($password, $iteratedSalt='', $iterations=1987, $binary_output=false) {
  109.         // Alias of MD5_MHA()
  110.  
  111.         return MD5_MHA($password, $iteratedSalt, $iterations, $binary_output);
  112. }
  113.  
  114. function MHA_AddIterations($mha, $iteratedSalt='', $additionalIterations, $binary_output=false) {
  115.         // This function converts a MHA with x itertions into a MHA with
  116.         // x+additionalIterations iterations, if the iteratedSalt is equal.
  117.         // Use this function if you want to upgrade your database to a higher MHA strength.
  118.         //
  119.         // Example:
  120.         // MHA_AddIterations(MHA('test', 'salt', 1987), 'salt', 13) == MHA('test', 'salt', 1987+13);
  121.         //
  122.         // Of course, you cannot lower the strength of a MHA, so additionalIterations has to be >= 0.
  123.  
  124.         // Is it Base64 input?
  125.         # if (strlen($mha) == 28) $mha = base64_decode($mha);
  126.         if (strlen($mha) == 27) $mha = base64_decode($mha);
  127.  
  128.         // Is it now binary input?
  129.         // (Que) Will there be problems if the input string looks like multibyte?
  130.         if (strlen($mha) != 20) {
  131.                 trigger_error('at function ' . __FUNCTION__ . ': does not seem to be a MHA', E_USER_ERROR);
  132.                 return false;
  133.         }
  134.  
  135.         if ($additionalIterations == 0) return $mha;
  136.  
  137.         if ($additionalIterations < 0) {
  138.                 trigger_error('at function ' . __FUNCTION__ . ': additionalIterations has to be 0 or higher.', E_USER_ERROR);
  139.                 return false;
  140.         }
  141.  
  142.         return marschallHash($mha, $iteratedSalt, $additionalIterations, $binary_output);
  143. }
  144.  
  145. ?>
  146.