Rev 1086 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
635 | daniel-mar | 1 | <?php |
2 | |||
3 | /* |
||
4 | * OIDplus 2.0 |
||
1084 | daniel-mar | 5 | * Copyright 2019 - 2023 Daniel Marschall, ViaThinkSoft |
635 | daniel-mar | 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 | |||
1050 | daniel-mar | 20 | namespace ViaThinkSoft\OIDplus; |
635 | daniel-mar | 21 | |
1086 | daniel-mar | 22 | // phpcs:disable PSR1.Files.SideEffects |
23 | \defined('INSIDE_OIDPLUS') or die; |
||
24 | // phpcs:enable PSR1.Files.SideEffects |
||
25 | |||
635 | daniel-mar | 26 | class OIDplusAuthPluginPhpGenericSaltedHex extends OIDplusAuthPlugin { |
27 | |||
1085 | daniel-mar | 28 | private function getBinaryHash($s_authmethod, $hashalgo, $salt, $check_password) { |
635 | daniel-mar | 29 | if ($s_authmethod == 'A1a') { |
30 | // This auth method can be used by you if you migrate users from another software solution into OIDplus |
||
1085 | daniel-mar | 31 | // A1a#hashalgo:X with X being H(salt+password) in hex- or base64-notation |
635 | daniel-mar | 32 | // Attention: With some hash algorithms, prepending the salt makes it vulnerable against length-extension-attacks |
1085 | daniel-mar | 33 | return hash($hashalgo, $salt.$check_password, true); |
635 | daniel-mar | 34 | } else if ($s_authmethod == 'A1b') { |
35 | // This auth method can be used by you if you migrate users from another software solution into OIDplus |
||
1085 | daniel-mar | 36 | // A1b#hashalgo:X with X being H(password+salt) in hex- or base64-notation |
37 | return hash($hashalgo, $check_password.$salt, true); |
||
635 | daniel-mar | 38 | } else if ($s_authmethod == 'A1c') { |
39 | // This auth method can be used by you if you migrate users from another software solution into OIDplus |
||
1085 | daniel-mar | 40 | // A1c#hashalgo:X with X being H(salt+password+salt) in hex- or base64-notation |
41 | return hash($hashalgo, $salt.$check_password.$salt, true); |
||
635 | daniel-mar | 42 | } else if ($s_authmethod == 'A1d') { |
43 | // This auth method can be used by you if you migrate users from another software solution into OIDplus |
||
1085 | daniel-mar | 44 | // A1d#hashalgo:X with X being H_HMAC(password,salt) in hex- or base64-notation |
45 | return hash_hmac($hashalgo, $check_password, $salt, true); |
||
635 | daniel-mar | 46 | } else { |
47 | // Invalid auth code |
||
48 | return false; |
||
49 | } |
||
1085 | daniel-mar | 50 | } |
635 | daniel-mar | 51 | |
1085 | daniel-mar | 52 | public function verify(OIDplusRAAuthInfo $authInfo, $check_password) { |
53 | $authKey = $authInfo->getAuthKey(); |
||
54 | $salt = $authInfo->getSalt(); |
||
55 | @list($s_authmethod, $s_authkey) = explode('#', $authKey, 2); |
||
56 | |||
57 | $hashalgo = explode(':', $s_authkey, 2)[0]; |
||
58 | |||
59 | $bindata = $this->getBinaryHash($s_authmethod, $hashalgo, $salt, $check_password); |
||
60 | if ($bindata === false) return false; |
||
61 | |||
1084 | daniel-mar | 62 | return hash_equals($s_authkey, $hashalgo.':'.strtolower(bin2hex($bindata))) |
63 | || hash_equals($s_authkey, $hashalgo.':'.base64_encode($bindata)) |
||
64 | || hash_equals($s_authkey, $hashalgo.':'.rtrim(base64_encode($bindata),'=')); |
||
635 | daniel-mar | 65 | } |
66 | |||
1088 | daniel-mar | 67 | private function getBestHashAlgo() { |
635 | daniel-mar | 68 | $preferred_hash_algos = array( |
69 | // sorted by priority |
||
1084 | daniel-mar | 70 | 'sha3-512', |
71 | 'sha3-384', |
||
635 | daniel-mar | 72 | 'sha3-256', |
73 | 'sha3-224', |
||
1084 | daniel-mar | 74 | 'sha512', |
635 | daniel-mar | 75 | 'sha512/256', |
76 | 'sha512/224', |
||
1084 | daniel-mar | 77 | 'sha384', |
635 | daniel-mar | 78 | 'sha256', |
79 | 'sha224', |
||
80 | 'sha1', |
||
81 | 'md5' |
||
82 | ); |
||
1085 | daniel-mar | 83 | |
84 | $s_authmethod = 'A1c'; |
||
85 | |||
635 | daniel-mar | 86 | $algos = hash_algos(); |
87 | $hashalgo = null; |
||
88 | foreach ($preferred_hash_algos as $a) { |
||
89 | if (in_array($a, $algos)) { |
||
90 | $hashalgo = $a; |
||
91 | break; |
||
92 | } |
||
93 | } |
||
1088 | daniel-mar | 94 | |
95 | return $hashalgo; |
||
96 | } |
||
97 | |||
98 | public function generate($password): OIDplusRAAuthInfo { |
||
99 | $s_authmethod = 'A1c'; |
||
100 | |||
101 | $hashalgo = $this->getBestHashAlgo(); |
||
635 | daniel-mar | 102 | if (is_null($hashalgo)) { |
103 | throw new OIDplusException(_L('No fitting hash algorithm found')); |
||
104 | } |
||
1088 | daniel-mar | 105 | |
1085 | daniel-mar | 106 | $salt = bin2hex(OIDplus::authUtils()->getRandomBytes(50)); // DB field ra.salt is limited to 100 chars (= 50 bytes) |
635 | daniel-mar | 107 | |
1085 | daniel-mar | 108 | $bindata = $this->getBinaryHash($s_authmethod, $hashalgo, $salt, $password); |
109 | if ($bindata === false) throw new OIDplusException(_L('Invalid hash auth method')); |
||
110 | |||
111 | $calc_authkey = $s_authmethod.'#'.$hashalgo.':'.strtolower(bin2hex($bindata)); |
||
112 | |||
1084 | daniel-mar | 113 | if (strlen($calc_authkey) > 100) { |
114 | // Since our database field is limited to 100 bytes, use base64 instead of hex |
||
1085 | daniel-mar | 115 | $calc_authkey = $s_authmethod.'#'.$hashalgo.':'.base64_encode($bindata); |
1084 | daniel-mar | 116 | $calc_authkey = rtrim($calc_authkey,'='); |
117 | /* |
||
1085 | daniel-mar | 118 | [Base64] sha3-512 authkey length in hex is 141 and length in base64 is 99 |
119 | [Base64] sha3-384 authkey length in hex is 109 and length in base64 is 77 |
||
120 | [Hex] sha3-256 authkey length in hex is 77 and length in base64 is 56 |
||
121 | [Hex] sha3-224 authkey length in hex is 69 and length in base64 is 51 |
||
122 | [Base64] sha512 authkey length in hex is 139 and length in base64 is 97 |
||
123 | [Hex] sha512/256 authkey length in hex is 79 and length in base64 is 58 |
||
124 | [Hex] sha512/224 authkey length in hex is 71 and length in base64 is 53 |
||
125 | [Base64] sha384 authkey length in hex is 107 and length in base64 is 75 |
||
126 | [Hex] sha256 authkey length in hex is 75 and length in base64 is 54 |
||
127 | [Hex] sha224 authkey length in hex is 67 and length in base64 is 49 |
||
128 | [Hex] sha1 authkey length in hex is 49 and length in base64 is 36 |
||
129 | [Hex] md5 authkey length in hex is 40 and length in base64 is 30 |
||
1084 | daniel-mar | 130 | */ |
131 | } |
||
132 | |||
1085 | daniel-mar | 133 | return new OIDplusRAAuthInfo($salt, $calc_authkey); |
635 | daniel-mar | 134 | } |
135 | |||
1088 | daniel-mar | 136 | public function available(&$reason): bool { |
137 | $hashalgo = $this->getBestHashAlgo(); |
||
138 | if (is_null($hashalgo)) { |
||
139 | $reason = _L('No fitting hash algorithm found'); |
||
140 | return false; |
||
141 | } else { |
||
142 | return true; |
||
143 | } |
||
144 | } |
||
145 | |||
635 | daniel-mar | 146 | } |