togethere.cloud/private_html/api/matches/ping-pong/1v1/internal/hmac.php

48 lines
1.4 KiB
PHP

<?php
function og_timing_safe_equals($a, $b) {
if (!is_string($a) || !is_string($b)) return false;
if (function_exists('hash_equals')) {
return hash_equals($a, $b);
}
if (strlen($a) !== strlen($b)) return false;
$res = 0;
for ($i = 0; $i < strlen($a); $i++) {
$res |= ord($a[$i]) ^ ord($b[$i]);
}
return $res === 0;
}
function og_hmac_sha256_hex($secret, $message) {
return hash_hmac('sha256', $message, $secret);
}
function og_require_node_signature($secret, $rawBody, $maxSkewMs = 60000) {
$ts = $_SERVER['HTTP_X_OG_TIMESTAMP'] ?? null;
$sigHeader = $_SERVER['HTTP_X_OG_SIGNATURE'] ?? '';
if (!$ts || !preg_match('/^\d{13,}$/', $ts)) {
return ['ok' => false, 'error' => 'missing_or_invalid_timestamp'];
}
if (!preg_match('/^sha256=([0-9a-f]{64})$/', $sigHeader, $m)) {
return ['ok' => false, 'error' => 'missing_or_invalid_signature'];
}
$now = (int) round(microtime(true) * 1000);
$tsInt = (int) $ts;
if (abs($now - $tsInt) > $maxSkewMs) {
return ['ok' => false, 'error' => 'timestamp_out_of_range'];
}
$msg = $ts . '.' . $rawBody;
$expected = og_hmac_sha256_hex($secret, $msg);
$provided = $m[1];
if (!og_timing_safe_equals($expected, $provided)) {
return ['ok' => false, 'error' => 'signature_mismatch'];
}
return ['ok' => true];
}