PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 5.6.0
Defender Security – Malware Scanner, Login Security & Firewall v5.6.0
6.3.0 6.2.3 6.2.4 6.2.0 6.2.1 6.2.2 6.1.0 5.3.1 5.4.0 5.4.1 5.5.0 5.5.1 5.6.0 5.6.1 5.6.2 5.7.0 5.7.1 5.7.2 5.8.0 5.8.1 5.9.0 6.0.0 6.0.1 3.0.1 3.1.0 All 141 releases
defender-security / vendor / altcha-org / altcha / src / Altcha.php

Altcha.php in Defender Security – Malware Scanner, Login Security & Firewall 5.6.0, at vendor/altcha-org/altcha/src/Altcha.php

222 lines 7.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace AltchaOrg\Altcha;
4
5 use InvalidArgumentException;
6
7 class Altcha
8 {
9 const DEFAULT_MAX_NUMBER = 1e6;
10 const DEFAULT_SALT_LENGTH = 12;
11 const DEFAULT_ALGORITHM = Algorithm::SHA256;
12
13 private static function randomBytes($length)
14 {
15 return random_bytes($length);
16 }
17
18 private static function randomInt($max)
19 {
20 return random_int(0, $max);
21 }
22
23 private static function hash($algorithm, $data)
24 {
25 switch ($algorithm) {
26 case Algorithm::SHA1:
27 return sha1($data, true);
28 case Algorithm::SHA256:
29 return hash('sha256', $data, true);
30 case Algorithm::SHA512:
31 return hash('sha512', $data, true);
32 default:
33 throw new InvalidArgumentException("Unsupported algorithm: $algorithm");
34 }
35 }
36
37 public static function hashHex($algorithm, $data)
38 {
39 return bin2hex(self::hash($algorithm, $data));
40 }
41
42 private static function hmacHash($algorithm, $data, $key)
43 {
44 switch ($algorithm) {
45 case Algorithm::SHA1:
46 return hash_hmac('sha1', $data, $key, true);
47 case Algorithm::SHA256:
48 return hash_hmac('sha256', $data, $key, true);
49 case Algorithm::SHA512:
50 return hash_hmac('sha512', $data, $key, true);
51 default:
52 throw new InvalidArgumentException("Unsupported algorithm: $algorithm");
53 }
54 }
55
56 private static function hmacHex($algorithm, $data, $key)
57 {
58 return bin2hex(self::hmacHash($algorithm, $data, $key));
59 }
60
61 private static function decodePayload($payload)
62 {
63 $decoded = base64_decode($payload);
64
65 if (!$decoded) {
66 return null;
67 }
68
69 try {
70 $data = json_decode($decoded, true, 2, JSON_THROW_ON_ERROR);
71 } catch (\JsonException|\ValueError $e) {
72 return null;
73 }
74
75 if (!is_array($data) || empty($data)) {
76 return null;
77 }
78
79 return $data;
80 }
81
82 public static function createChallenge($options)
83 {
84 if (is_array($options)) {
85 $options = new ChallengeOptions($options);
86 }
87
88 $algorithm = $options->algorithm ?: self::DEFAULT_ALGORITHM;
89 $maxNumber = $options->maxNumber ?: self::DEFAULT_MAX_NUMBER;
90 $saltLength = $options->saltLength ?: self::DEFAULT_SALT_LENGTH;
91
92 $params = $options->params;
93 if ($options->expires) {
94 $params['expires'] = $options->expires->getTimestamp();
95 }
96
97 $salt = $options->salt ?: bin2hex(self::randomBytes($saltLength));
98 if (!empty($params)) {
99 $salt .= '?' . http_build_query($params);
100 }
101
102 $number = $options->number ?: self::randomInt($maxNumber);
103
104 $challenge = self::hashHex($algorithm, $salt . $number);
105
106 $signature = self::hmacHex($algorithm, $challenge, $options->hmacKey);
107
108 return new Challenge($algorithm, $challenge, $maxNumber, $salt, $signature);
109 }
110
111 public static function verifySolution($payload, $hmacKey, $checkExpires = true)
112 {
113 if (is_string($payload)) {
114 $payload = self::decodePayload($payload);
115 }
116
117 if ($payload === null
118 || !isset($payload['algorithm'], $payload['challenge'], $payload['number'], $payload['salt'], $payload['signature'])
119 ) {
120 return false;
121 }
122
123 $payload = new Payload($payload['algorithm'], $payload['challenge'], $payload['number'], $payload['salt'], $payload['signature']);
124
125 $params = self::extractParams($payload);
126 if ($checkExpires && isset($params['expires'])) {
127 $expireTime = (int)$params['expires'];
128 if (time() > $expireTime) {
129 return false;
130 }
131 }
132
133 $challengeOptions = new ChallengeOptions([
134 'algorithm' => $payload->algorithm,
135 'hmacKey' => $hmacKey,
136 'number' => $payload->number,
137 'salt' => $payload->salt,
138 ]);
139
140 $expectedChallenge = self::createChallenge($challengeOptions);
141
142 return $expectedChallenge->challenge === $payload->challenge &&
143 $expectedChallenge->signature === $payload->signature;
144 }
145
146 private static function extractParams($payload)
147 {
148 $saltParts = explode('?', $payload->salt);
149 if (count($saltParts) > 1) {
150 parse_str($saltParts[1], $params);
151 return $params;
152 }
153 return [];
154 }
155
156 public static function verifyFieldsHash($formData, $fields, $fieldsHash, $algorithm)
157 {
158 $lines = [];
159 foreach ($fields as $field) {
160 $lines[] = $formData[$field] ?? '';
161 }
162 $joinedData = implode("\n", $lines);
163 $computedHash = self::hashHex($algorithm, $joinedData);
164 return $computedHash === $fieldsHash;
165 }
166
167 public static function verifyServerSignature($payload, $hmacKey)
168 {
169 if (is_string($payload)) {
170 $payload = self::decodePayload($payload);
171 }
172
173 if ($payload === null
174 || !isset($payload['algorithm'], $payload['verificationData'], $payload['signature'], $payload['verified'])
175 ) {
176 return false;
177 }
178
179 $payload = new ServerSignaturePayload($payload['algorithm'], $payload['verificationData'], $payload['signature'], $payload['verified']);
180
181 $hash = self::hash($payload->algorithm, $payload->verificationData);
182 $expectedSignature = self::hmacHex($payload->algorithm, $hash, $hmacKey);
183
184 parse_str($payload->verificationData, $params);
185
186 $verificationData = new ServerSignatureVerificationData();
187 $verificationData->classification = $params['classification'] ?? '';
188 $verificationData->country = $params['country'] ?? '';
189 $verificationData->detectedLanguage = $params['detectedLanguage'] ?? '';
190 $verificationData->email = $params['email'] ?? '';
191 $verificationData->expire = (int)($params['expire'] ?? 0);
192 $verificationData->fields = explode(',', $params['fields'] ?? '');
193 $verificationData->fieldsHash = $params['fieldsHash'] ?? '';
194 $verificationData->reasons = explode(',', $params['reasons'] ?? '');
195 $verificationData->score = (float)($params['score'] ?? 0);
196 $verificationData->time = (int)($params['time'] ?? 0);
197 $verificationData->verified = ($params['verified'] ?? 'false') === 'true';
198
199 $now = time();
200 $isVerified = $payload->verified && $verificationData->verified &&
201 $verificationData->expire > $now &&
202 $payload->signature === $expectedSignature;
203
204 return [$isVerified, $verificationData];
205 }
206
207 public static function solveChallenge($challenge, $salt, $algorithm, $max = 1000000, $start = 0)
208 {
209 $startTime = microtime(true);
210
211 for ($n = $start; $n <= $max; $n++) {
212 $hash = self::hashHex($algorithm, $salt . $n);
213 if ($hash === $challenge) {
214 $took = microtime(true) - $startTime;
215 return new Solution($n, $took);
216 }
217 }
218
219 return null;
220 }
221 }
222