Subversion Repositories oidplus

Rev

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

Rev Author Line No. Line
1088 daniel-mar 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
 
1116 daniel-mar 28
        /**
1212 daniel-mar 29
         * @return string
30
         */
31
        public function id(): string {
32
                return 'A4_argon2';
33
        }
34
 
35
        /**
1116 daniel-mar 36
         * @param bool $html
37
         * @return void
38
         */
39
        public function init(bool $html=true) {
1088 daniel-mar 40
                // TODO: Let the admin decide about the memory, iterations, and parallelism options
41
        }
42
 
1116 daniel-mar 43
        /**
44
         * @param string $authKey
45
         * @return bool
46
         */
47
        private function supportedCryptAlgo(string $authKey): bool {
1088 daniel-mar 48
                return str_starts_with($authKey, '$argon2i$') ||
49
                       str_starts_with($authKey, '$argon2id$');
50
        }
51
 
1116 daniel-mar 52
        /**
53
         * @param OIDplusRAAuthInfo $authInfo
54
         * @param string $check_password
55
         * @return bool
56
         */
57
        public function verify(OIDplusRAAuthInfo $authInfo, string $check_password): bool {
1088 daniel-mar 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
 
1116 daniel-mar 72
        /**
73
         * @return string|int|false
74
         */
75
        private function getBestHashAlgo() { /* @phpstan-ignore-line */
1088 daniel-mar 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
 
1116 daniel-mar 86
        /**
87
         * @param string $password
88
         * @return OIDplusRAAuthInfo
89
         * @throws OIDplusException
90
         */
91
        public function generate(string $password): OIDplusRAAuthInfo {
1088 daniel-mar 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));
1090 daniel-mar 97
                return new OIDplusRAAuthInfo($calc_authkey);
1088 daniel-mar 98
        }
99
 
1116 daniel-mar 100
        /**
101
         * @return bool
102
         */
1088 daniel-mar 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
 
1116 daniel-mar 111
        /**
112
         * @return bool
113
         */
1088 daniel-mar 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
 
1116 daniel-mar 122
        /**
123
         * @param string $reason
124
         * @return bool
125
         */
126
        public function availableForHash(string &$reason): bool {
1088 daniel-mar 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
 
1116 daniel-mar 135
        /**
136
         * @param string $reason
137
         * @return bool
138
         */
139
        public function availableForVerify(string &$reason): bool {
1099 daniel-mar 140
                return $this->availableForHash($reason);
141
        }
142
 
1088 daniel-mar 143
}