Subversion Repositories oidplus

Rev

Rev 1116 | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. <?php
  2.  
  3. /*
  4.  * OIDplus 2.0
  5.  * Copyright 2019 - 2023 Daniel Marschall, ViaThinkSoft
  6.  *
  7.  * Licensed under the Apache License, Version 2.0 (the "License");
  8.  * you may not use this file except in compliance with the License.
  9.  * You may obtain a copy of the License at
  10.  *
  11.  *     http://www.apache.org/licenses/LICENSE-2.0
  12.  *
  13.  * Unless required by applicable law or agreed to in writing, software
  14.  * distributed under the License is distributed on an "AS IS" BASIS,
  15.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16.  * See the License for the specific language governing permissions and
  17.  * limitations under the License.
  18.  */
  19.  
  20. namespace ViaThinkSoft\OIDplus;
  21.  
  22. // phpcs:disable PSR1.Files.SideEffects
  23. \defined('INSIDE_OIDPLUS') or die;
  24. // phpcs:enable PSR1.Files.SideEffects
  25.  
  26. class OIDplusAuthPluginArgon2 extends OIDplusAuthPlugin {
  27.  
  28.         /**
  29.          * @return string
  30.          */
  31.         public function id(): string {
  32.                 return 'A4_argon2';
  33.         }
  34.  
  35.         /**
  36.          * @param bool $html
  37.          * @return void
  38.          */
  39.         public function init(bool $html=true) {
  40.                 // TODO: Let the admin decide about the memory, iterations, and parallelism options
  41.         }
  42.  
  43.         /**
  44.          * @param string $authKey
  45.          * @return bool
  46.          */
  47.         private function supportedCryptAlgo(string $authKey): bool {
  48.                 return str_starts_with($authKey, '$argon2i$') ||
  49.                        str_starts_with($authKey, '$argon2id$');
  50.         }
  51.  
  52.         /**
  53.          * @param OIDplusRAAuthInfo $authInfo
  54.          * @param string $check_password
  55.          * @return bool
  56.          */
  57.         public function verify(OIDplusRAAuthInfo $authInfo, string $check_password): bool {
  58.                 $authKey = $authInfo->getAuthKey();
  59.  
  60.                 if (!$this->supportedCryptAlgo($authKey)) {
  61.                         // Unsupported algorithm
  62.                         return false;
  63.                 }
  64.  
  65.                 // $argon2i$v=19$m=1024,t=2,p=2$MEhSZkJLQXUxRzljNE5hMw$33pvelMsxqOn/1VV2pnjmKJUECBhilzOZ2+Gq/FxCP4
  66.                 //  \_____/ \__/ \____________/ \____________________/ \_________________________________________/
  67.                 //   Algo   Vers  Cost options   Salt                   Hash
  68.  
  69.                 return password_verify($check_password, $authKey);
  70.         }
  71.  
  72.         /**
  73.          * @return string|int|false
  74.          */
  75.         private function getBestHashAlgo() { /* @phpstan-ignore-line */
  76.                 if ($this->supportsArgon2id()) {
  77.                         $hashalgo = PASSWORD_ARGON2ID;
  78.                 } else if ($this->supportsArgon2i()) {
  79.                         $hashalgo = PASSWORD_ARGON2I;
  80.                 } else {
  81.                         $hashalgo = false;
  82.                 }
  83.                 return $hashalgo;
  84.         }
  85.  
  86.         /**
  87.          * @param string $password
  88.          * @return OIDplusRAAuthInfo
  89.          * @throws OIDplusException
  90.          */
  91.         public function generate(string $password): OIDplusRAAuthInfo {
  92.                 $hashalgo = $this->getBestHashAlgo();
  93.                 assert($hashalgo !== false); // Should not happen if we called available() before!
  94.                 $calc_authkey = password_hash($password, $hashalgo);
  95.                 if (!$calc_authkey) throw new OIDplusException(_L('Error creating password hash'));
  96.                 assert($this->supportedCryptAlgo($calc_authkey));
  97.                 return new OIDplusRAAuthInfo($calc_authkey);
  98.         }
  99.  
  100.         /**
  101.          * @return bool
  102.          */
  103.         private function supportsArgon2i(): bool {
  104.                 if (version_compare(PHP_VERSION, '7.4.0') >= 0) {
  105.                         return in_array('argon2i', password_algos());
  106.                 } else {
  107.                         return defined('PASSWORD_ARGON2I');
  108.                 }
  109.         }
  110.  
  111.         /**
  112.          * @return bool
  113.          */
  114.         private function supportsArgon2id(): bool {
  115.                 if (version_compare(PHP_VERSION, '7.4.0') >= 0) {
  116.                         return in_array('argon2id', password_algos());
  117.                 } else {
  118.                         return defined('PASSWORD_ARGON2ID');
  119.                 }
  120.         }
  121.  
  122.         /**
  123.          * @param string $reason
  124.          * @return bool
  125.          */
  126.         public function availableForHash(string &$reason): bool {
  127.                 if (!$this->supportsArgon2i() && !$this->supportsArgon2id()) {
  128.                         $reason = _L('No fitting hash algorithm found');
  129.                         return false;
  130.                 } else {
  131.                         return true;
  132.                 }
  133.         }
  134.  
  135.         /**
  136.          * @param string $reason
  137.          * @return bool
  138.          */
  139.         public function availableForVerify(string &$reason): bool {
  140.                 return $this->availableForHash($reason);
  141.         }
  142.  
  143. }
  144.