Subversion Repositories oidplus

Rev

Rev 1116 | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

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