-
Notifications
You must be signed in to change notification settings - Fork 0
/
verifyYubicoOTP.php
67 lines (57 loc) · 1.92 KB
/
verifyYubicoOTP.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<?php
/**
Yubico OTP v2 Verifier
@param $otp Yubico OTP
@param $apiClientId YubiCloud API Client ID
@param $apiSecretKey YubiCloud API Secret Key
@return YubiKey Public ID if validation is successful, false if not
@version 1.0.2
@copyright (c) 2022 M. Taniguchi
@license MIT License
*/
function verifyYubicoOTP(string $otp, string $apiClientId, string $apiSecretKey) : ?string {
// Generate API call URL
$nonce = md5(rand(0, 0x7fffffff));
$url = 'https://api.yubico.com/wsapi/2.0/verify?id=' . urlencode($apiClientId) . '&otp=' . urlencode($otp) . '&nonce=' . urlencode($nonce);
// Execute cURL
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
$params = curl_exec($ch);
curl_close($ch);
if (!$params) return null; // Returns null if communication fails
// Make the returned information into an associative array
$data = array();
$params = explode("\n", $params);
forEach($params as $param) {
if (!$param) continue;
if (($p = strpos($param, '=')) === false) continue;
$data[substr($param, 0, $p)] = substr($param, $p + 1, -1);
}
ksort($data); // Sort by key (required for specification)
// Reconstruct information into GET parameter format and verify HMAC-SHA-1 signatures
$params = '';
$hash = $id = $status = null;
forEach($data as $key => $val) {
switch ($key) {
case 'h':
$hash = $val;
continue 2;
case 'status':
$status = $val;
break;
case 'otp':
$id = substr($val, 0, strlen($val) - 32);
break;
case 'nonce':
if ($nonce == $val) $nonce = true;
break;
}
if ($params) $params .= '&';
$params .= $key . '=' . $val;
}
$hash = ($hash === base64_encode(hash_hmac('sha1', $params, base64_decode($apiSecretKey), true)));
// Returns Public ID of YubiKey on successful verification, null on failure
return ($hash && $nonce === true && $status === 'OK' && $id)? $id : null;
}