Subversion Repositories php_utils

Rev

Rev 59 | Rev 63 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. <?php
  2.  
  3. /*
  4.  * ViaThinkSoft Modular Crypt Format 1.0
  5.  * Revision 2023-02-26
  6.  * Copyright 2023 Daniel Marschall, ViaThinkSoft
  7.  *
  8.  * Licensed under the Apache License, Version 2.0 (the "License");
  9.  * you may not use this file except in compliance with the License.
  10.  * You may obtain a copy of the License at
  11.  *
  12.  *     http://www.apache.org/licenses/LICENSE-2.0
  13.  *
  14.  * Unless required by applicable law or agreed to in writing, software
  15.  * distributed under the License is distributed on an "AS IS" BASIS,
  16.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17.  * See the License for the specific language governing permissions and
  18.  * limitations under the License.
  19.  */
  20.  
  21. /*
  22.  
  23. ViaThinkSoft Modular Crypt Format 1.0 performs a simple hash or HMAC operation.
  24. No key derivation function or iterations are performed.
  25.  
  26. Format:
  27.         $1.3.6.1.4.1.37476.3.0.1.1$a=<algo>,m=<mode>$<salt>$<hash>
  28.  
  29. where <algo> is any valid hash algorithm (name scheme of PHP hash_algos() preferred), e.g.
  30.         sha3-512
  31.         sha3-384
  32.         sha3-256
  33.         sha3-224
  34.         sha512
  35.         sha512/256
  36.         sha512/224
  37.         sha384
  38.         sha256
  39.         sha224
  40.         sha1
  41.         md5
  42.  
  43. Valid <mode> :
  44.         sp = salt + password
  45.         ps = password + salt
  46.         sps = salt + password + salt
  47.         hmac = HMAC (salt is the key)
  48.  
  49. Like most Crypt-hashes, <salt> and <hash> are Radix64 coded
  50. with alphabet './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' and no padding.
  51.  
  52. Link to the online specification:
  53.         https://oidplus.viathinksoft.com/oidplus/?goto=oid%3A1.3.6.1.4.1.37476.3.0.1.1
  54.  
  55. Reference implementation in PHP:
  56.         https://github.com/danielmarschall/php_utils/blob/master/vts_crypt.inc.php
  57.  
  58. */
  59.  
  60. define('OID_MCF_VTS_V1', '1.3.6.1.4.1.37476.3.0.1.1'); // { iso(1) identified-organization(3) dod(6) internet(1) private(4) enterprise(1) 37476 specifications(3) misc(0) modular-crypt-format(1) vts-crypt-v1(1) }
  61.  
  62. define('BASE64_RFC4648_ALPHABET', '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/');
  63. define('BASE64_CRYPT_ALPHABET',   './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz');
  64.  
  65. function crypt_radix64_encode($str) {
  66.         $x = $str;
  67.         $x = base64_encode($x);
  68.         $x = rtrim($x, '=');
  69.         $x = strtr($x, BASE64_RFC4648_ALPHABET, BASE64_CRYPT_ALPHABET);
  70.         return $x;
  71. }
  72.  
  73. function crypt_radix64_decode($str) {
  74.         $x = $str;
  75.         $x = strtr($x, BASE64_CRYPT_ALPHABET, BASE64_RFC4648_ALPHABET);
  76.         $x = base64_decode($x);
  77.         return $x;
  78. }
  79.  
  80. // assert(crypt_radix64_decode(crypt_radix64_encode('TEST123')) === 'TEST123');
  81.  
  82. function crypt_modular_format($id, $bin_salt, $bin_hash, $params=null) {
  83.         // $<id>[$<param>=<value>(,<param>=<value>)*][$<salt>[$<hash>]]
  84.         $out = '$'.$id;
  85.         if (!is_null($params)) {
  86.                 $ary_params = array();
  87.                 //ksort($params);
  88.                 foreach ($params as $name => $value) {
  89.                         $ary_params[] = "$name=$value";
  90.                 }
  91.                 $out .= '$'.implode(',',$ary_params);
  92.         }
  93.         $out .= '$'.crypt_radix64_encode($bin_salt);
  94.         $out .= '$'.crypt_radix64_encode($bin_hash);
  95.         return $out;
  96. }
  97.  
  98. function crypt_modular_format_decode($mcf) {
  99.         $ary = explode('$', $mcf);
  100.  
  101.         $dummy = array_shift($ary);
  102.         if ($dummy !== '') return false;
  103.  
  104.         $dummy = array_shift($ary);
  105.         $id = $dummy;
  106.  
  107.         $params = array();
  108.         $dummy = array_shift($ary);
  109.         if (strpos($dummy, '=') !== false) {
  110.                 $params_ary = explode(',',$dummy);
  111.                 foreach ($params_ary as $param) {
  112.                         $bry = explode('=', $param, 2);
  113.                         if (count($bry) > 1) {
  114.                                 $params[$bry[0]] = $bry[1];
  115.                         }
  116.                 }
  117.         } else {
  118.                 array_unshift($ary, $dummy);
  119.         }
  120.  
  121.         if (count($ary) > 1) {
  122.                 $dummy = array_shift($ary);
  123.                 $bin_salt = crypt_radix64_decode($dummy);
  124.         } else {
  125.                 $bin_salt = '';
  126.         }
  127.  
  128.         $dummy = array_shift($ary);
  129.         $bin_hash = crypt_radix64_decode($dummy);
  130.  
  131.         return array('id' => $id, 'salt' => $bin_salt, 'hash' => $bin_hash, 'params' => $params);
  132. }
  133.  
  134. function vts_crypt($algo, $str_password, $str_salt, $ver='1', $mode='ps') {
  135.         if ($ver == '1') {
  136.                 if ($mode == 'sp') {
  137.                         $payload = $str_salt.$str_password;
  138.                         if (($algo === 'sha3-512') && !in_array($algo, hash_algos()) && function_exists('sha3_512')) {
  139.                                 $bin_hash = sha3_512($payload, true);
  140.                         } else {
  141.                                 $bin_hash = hash($algo, $payload, true);
  142.                         }
  143.                 } else if ($mode == 'ps') {
  144.                         $payload = $str_password.$str_salt;
  145.                         if (($algo === 'sha3-512') && !in_array($algo, hash_algos()) && function_exists('sha3_512')) {
  146.                                 $bin_hash = sha3_512($payload, true);
  147.                         } else {
  148.                                 $bin_hash = hash($algo, $payload, true);
  149.                         }
  150.                 } else if ($mode == 'sps') {
  151.                         $payload = $str_salt.$str_password.$str_salt;
  152.                         if (($algo === 'sha3-512') && !in_array($algo, hash_algos()) && function_exists('sha3_512')) {
  153.                                 $bin_hash = sha3_512($payload, true);
  154.                         } else {
  155.                                 $bin_hash = hash($algo, $payload, true);
  156.                         }
  157.                 } else if ($mode == 'hmac') {
  158.                         // Note: Actually, we should use hash_hmac_algos(), but this requires PHP 7.2, and we would like to stay compatible with PHP 7.0 for now
  159.                         if (($algo === 'sha3-512') && !in_array($algo, hash_algos()) && function_exists('sha3_512_hmac')) {
  160.                                 $bin_hash = sha3_512_hmac($str_password, $str_salt, true);
  161.                         } else {
  162.                                 $bin_hash = hash_hmac($algo, $str_password, $str_salt, true);
  163.                         }
  164.                 } else {
  165.                         throw new Exception("Invalid VTS crypt version 1 mode. Expect sp, ps, sps, or hmac.");
  166.                 }
  167.                 $bin_salt = $str_salt;
  168.                 return crypt_modular_format(OID_MCF_VTS_V1, $bin_salt, $bin_hash, array('a'=>$algo,'m'=>$mode));
  169.         } else {
  170.                 throw new Exception("Invalid VTS crypt version, expect 1.");
  171.         }
  172. }
  173.