Subversion Repositories php_utils

Rev

Rev 59 | 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. Link to the online specification:
  50.         https://oidplus.viathinksoft.com/oidplus/?goto=oid%3A1.3.6.1.4.1.37476.3.0.1.1
  51.  
  52. Reference implementation in PHP:
  53.         https://github.com/danielmarschall/php_utils/blob/master/vts_crypt.inc.php
  54.  
  55. */
  56.  
  57. 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) }
  58.  
  59. function crypt_radix64($str) {
  60.         $rfc4648_base64 = array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '+', '/', '=');
  61.         $crypt_base64   = array('.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '');
  62.  
  63.         $x = base64_encode($str);
  64.         $x = str_replace($rfc4648_base64, $crypt_base64, $x);
  65.         return $x;
  66. }
  67.  
  68. function crypt_modular_format($id, $bin_salt, $bin_hash, $params=null) {
  69.         // $<id>[$<param>=<value>(,<param>=<value>)*][$<salt>[$<hash>]]
  70.         $out = '$'.$id;
  71.         if (!is_null($params)) {
  72.                 $ary_params = array();
  73.                 //ksort($params);
  74.                 foreach ($params as $name => $value) {
  75.                         $ary_params[] = "$name=$value";
  76.                 }
  77.                 $out .= '$'.implode(',',$ary_params);
  78.         }
  79.         $out .= '$'.crypt_radix64($bin_salt);
  80.         $out .= '$'.crypt_radix64($bin_hash);
  81.         return $out;
  82. }
  83.  
  84. function vts_crypt($algo, $str_password, $str_salt, $ver='1', $mode='ps') {
  85.         if ($ver == '1') {
  86.                 if ($mode == 'sp') {
  87.                         $payload = $str_salt.$str_password;
  88.                         if (($algo === 'sha3-512') && !in_array($algo, hash_algos()) && function_exists('sha3_512')) {
  89.                                 $bin_hash = sha3_512($payload, true);
  90.                         } else {
  91.                                 $bin_hash = hash($algo, $payload, true);
  92.                         }
  93.                 } else if ($mode == 'ps') {
  94.                         $payload = $str_password.$str_salt;
  95.                         if (($algo === 'sha3-512') && !in_array($algo, hash_algos()) && function_exists('sha3_512')) {
  96.                                 $bin_hash = sha3_512($payload, true);
  97.                         } else {
  98.                                 $bin_hash = hash($algo, $payload, true);
  99.                         }
  100.                 } else if ($mode == 'sps') {
  101.                         $payload = $str_salt.$str_password.$str_salt;
  102.                         if (($algo === 'sha3-512') && !in_array($algo, hash_algos()) && function_exists('sha3_512')) {
  103.                                 $bin_hash = sha3_512($payload, true);
  104.                         } else {
  105.                                 $bin_hash = hash($algo, $payload, true);
  106.                         }
  107.                 } else if ($mode == 'hmac') {
  108.                         // 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
  109.                         if (($algo === 'sha3-512') && !in_array($algo, hash_algos()) && function_exists('sha3_512_hmac')) {
  110.                                 $bin_hash = sha3_512_hmac($str_password, $str_salt, true);
  111.                         } else {
  112.                                 $bin_hash = hash_hmac($algo, $str_password, $str_salt, true);
  113.                         }
  114.                 } else {
  115.                         throw new Exception("Invalid VTS crypt version 1 mode. Expect sp, ps, sps, or hmac.");
  116.                 }
  117.                 $bin_salt = $str_salt;
  118.                 return crypt_modular_format(OID_MCF_VTS_V1, $bin_salt, $bin_hash, array('a'=>$algo,'m'=>$mode));
  119.         } else {
  120.                 throw new Exception("Invalid VTS crypt version, except 1.");
  121.         }
  122. }
  123.  
  124. function vts_crypt_convert_from_old_oidplus($authkey, $salt) {
  125.         if (preg_match('@^A1([abcd])#(.+):(.+)$@', $authkey, $m)) {
  126.                 // A1a#hashalgo:X with X being H(salt+password) in hex- or base64-notation
  127.                 // A1b#hashalgo:X with X being H(password+salt) in hex- or base64-notation
  128.                 // A1c#hashalgo:X with X being H(salt+password+salt) in hex- or base64-notation
  129.                 // A1d#hashalgo:X with X being H_HMAC(password,salt) in hex- or base64-notation
  130.                 $mode = ''; // avoid PHPstan warning
  131.                 if ($m[1] == 'a') $mode = 'sp';
  132.                 else if ($m[1] == 'b') $mode = 'ps';
  133.                 else if ($m[1] == 'c') $mode = 'sps';
  134.                 else if ($m[1] == 'd') $mode = 'hmac';
  135.                 else assert(false);
  136.                 $algo = $m[2];
  137.                 $bin_salt = $salt;
  138.                 if (($algo == 'sha3-512') || ($algo == 'sha3-384') || ($algo == 'sha512') || ($algo == 'sha384')) {
  139.                         $bin_hash = base64_decode($m[3]);
  140.                 } else {
  141.                         $bin_hash = hex2bin($m[3]);
  142.                 }
  143.                 return crypt_modular_format(OID_MCF_VTS_V1, $bin_salt, $bin_hash, array('a'=>$algo,'m'=>$mode));
  144.         } else if (preg_match('@^A2#(.+)$@', $authkey, $m)) {
  145.                 // A2#X with X being sha3(salt+password) in base64-notation
  146.                 $mode = 'sp';
  147.                 $algo = 'sha3-512';
  148.                 $bin_salt = $salt;
  149.                 $bin_hash = base64_decode($m[1]);
  150.                 return crypt_modular_format(OID_MCF_VTS_V1, $bin_salt, $bin_hash, array('a'=>$algo,'m'=>$mode));
  151.         } else if (preg_match('@^A3#(.+)$@', $authkey, $m)) {
  152.                 // A3#X with X being bcrypt  [not VTS hash!]
  153.                 return $m[1];
  154.         } else {
  155.                 // Nothing to convert
  156.                 return $authkey;
  157.         }
  158. }
  159.