PHP
299 lines · 8,542 bytes
<?php
class LibreAuth
{
public $name;
public $ownerid;
public $version;
public $url;
public $sessionid;
public $enckey;
private $nonce;
public $hash;
public function __construct($name, $ownerid, $version, $url)
{
$this->name = $name;
$this->ownerid = $ownerid;
$this->version = $version;
$this->url = rtrim($url, '/') . '/';
}
public function init($enckey = null)
{
if ($enckey === null) {
$enckey = substr(bin2hex(random_bytes(18)), 0, 35);
}
$this->enckey = substr($enckey, 0, 35);
$res = $this->req([
'type' => 'init',
'ver' => $this->version,
'enckey' => $this->enckey,
'hash' => $this->hash ?? '',
]);
if (!$res['success']) die($res['message']);
$this->sessionid = $res['sessionid'];
return $res;
}
public function loginEmail($email, $password, $hwid = '', $code = null)
{
$data = [
'type' => 'loginEmail',
'email' => $email,
'pass' => $password,
'hwid' => $hwid,
];
if ($code) $data['code'] = $code;
return $this->auth($data);
}
public function login($username, $password, $hwid = '', $code = null)
{
$data = [
'type' => 'login',
'username' => $username,
'pass' => $password,
'hwid' => $hwid,
];
if ($code) $data['code'] = $code;
return $this->auth($data);
}
public function register($username, $password, $key, $hwid = '', $email = '')
{
$data = [
'type' => 'register',
'username' => $username,
'pass' => $password,
'key' => $key,
'hwid' => $hwid,
];
if ($email) $data['email'] = $email;
return $this->auth($data);
}
public function license($key, $hwid = '', $code = null)
{
$data = ['type' => 'license', 'key' => $key, 'hwid' => $hwid];
if ($code) $data['code'] = $code;
return $this->auth($data);
}
public function upgrade($username, $key)
{
$res = $this->req(['type' => 'upgrade', 'username' => $username, 'key' => $key]);
return $res['success'];
}
public function forgot($username, $email)
{
$res = $this->req(['type' => 'forgot', 'username' => $username, 'email' => $email]);
return $res['success'];
}
public function logout()
{
$this->req(['type' => 'logout']);
$this->sessionid = null;
}
public function check()
{
$res = $this->req(['type' => 'check']);
return $res['success'];
}
public function ban($reason = '')
{
$res = $this->req(['type' => 'ban', 'reason' => $reason]);
return $res['success'];
}
public function var($varid)
{
$res = $this->req(['type' => 'var', 'varid' => $varid]);
return $res['success'] ? $res['message'] : null;
}
public function getvar($var)
{
$res = $this->req(['type' => 'getvar', 'var' => $var]);
return $res['success'] ? $res['response'] : null;
}
public function setvar($var, $data)
{
$this->req(['type' => 'setvar', 'var' => $var, 'data' => $data]);
}
public function log($message, $pcuser = '')
{
$this->req(['type' => 'log', 'message' => $message, 'pcuser' => $pcuser]);
}
public function webhook($webid, $params, $body = '', $conttype = '')
{
$res = $this->req([
'type' => 'webhook',
'webid' => $webid,
'params' => $params,
'body' => $body,
'conttype' => $conttype,
]);
return $res['success'] ? $res['response'] : null;
}
public function file($fileid)
{
$res = $this->req(['type' => 'file', 'fileid' => $fileid]);
if (!$res['success']) return null;
return hex2bin($res['contents']);
}
public function fetchOnline()
{
$res = $this->req(['type' => 'fetchOnline']);
return $res['success'] ? $res['users'] : null;
}
public function fetchStats()
{
return $this->req(['type' => 'fetchStats']);
}
public function checkblack()
{
$res = $this->req(['type' => 'checkblacklist', 'hwid' => '']);
return $res['success'];
}
public function chatget($channel)
{
$res = $this->req(['type' => 'chatget', 'channel' => $channel]);
return $res['success'] ? $res['messages'] : null;
}
public function chatsend($message, $channel)
{
$res = $this->req(['type' => 'chatsend', 'message' => $message, 'channel' => $channel]);
return $res['success'];
}
public function changeUsername($newUsername)
{
$res = $this->req(['type' => 'changeUsername', 'newUsername' => $newUsername]);
return $res['success'];
}
public function enable2fa($code = null)
{
$data = ['type' => '2faenable'];
if ($code) $data['code'] = $code;
return $this->req($data);
}
public function disable2fa($code)
{
return $this->req(['type' => '2fadisable', 'code' => $code]);
}
private function auth($data)
{
$res = $this->req($data);
if (!$res['success']) return false;
return $res['info'];
}
private function req($data)
{
$data['name'] = $this->name;
$data['ownerid'] = $this->ownerid;
if ($this->sessionid) $data['sessionid'] = $this->sessionid;
if ($this->nonce) $data['nonce'] = $this->nonce;
$ch = curl_init($this->url);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query($data),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HEADER => true,
CURLOPT_TIMEOUT => 20,
CURLOPT_USERAGENT => 'LibreAuth',
]);
$raw = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$hsize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
curl_close($ch);
if ($code === 429) die('Rate limited');
$body = substr($raw, $hsize);
if ($body === 'LibreAuth_Invalid') die('LibreAuth_Invalid');
$headers = [];
foreach (explode("\r\n", substr($raw, 0, $hsize)) as $line) {
$p = explode(': ', $line, 2);
if (isset($p[1])) $headers[strtolower($p[0])] = trim($p[1]);
}
$this->verifySig($body, $headers, $data['type']);
if ($this->enckey && $this->looksEnc($body)) {
$body = $this->decryptBody($body, $this->enckey);
}
$json = json_decode($body, true) ?: ['success' => false, 'message' => 'Bad response'];
if (isset($json['ownerid']) && $json['ownerid'] !== $this->ownerid) {
die('Application mismatch');
}
if (!empty($json['nonce'])) $this->nonce = $json['nonce'];
return $json;
}
private function looksEnc($body)
{
if ($body === '' || ($body[0] ?? '') === '{') return false;
return base64_decode($body, true) !== false;
}
private function decryptBody($blob, $enckey)
{
$raw = base64_decode($blob, true);
if ($raw === false || strlen($raw) < 17) return $blob;
$iv = substr($raw, 0, 16);
$cipher = substr($raw, 16);
$key = hash('sha256', $enckey, true);
$out = openssl_decrypt($cipher, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
return $out !== false ? $out : $blob;
}
private function verifySig($body, $headers, $type)
{
$skip = ['log', 'file', '2faenable', '2fadisable'];
if (in_array($type, $skip, true)) return;
global \$LibreAuthPublicKey;
if (empty(\$LibreAuthPublicKey) || !extension_loaded('sodium')) return;
$ts = $headers['x-signature-timestamp'] ?? null;
$sig = $headers['x-signature-ed25519'] ?? null;
if (!$ts || !$sig) return;
if (abs(time() - (int) $ts) > 300) return;
try {
$ok = sodium_crypto_sign_verify_detached(
sodium_hex2bin($sig),
$ts . $body,
sodium_hex2bin(\$LibreAuthPublicKey)
);
if (!$ok) die('Signature verification failed');
} catch (Throwable $e) {
die('Signature verification failed');
}
}
}