diff --git a/skysparkClass.php b/skysparkClass.php index 429c15d..d4d32fa 100644 --- a/skysparkClass.php +++ b/skysparkClass.php @@ -19,11 +19,14 @@ * Techledge | SkySparkAuth Class * * Conducts SCRAM Authentication for SS3. -*****************************************************/ +***************************************************** +* Updated: 2025-07-22 +* Updated by: Shaun R Hawk(shaun@shaunhawk.com) || Huckleberry Services, FSM Controls, LLC. +* SkySpark Class is used to authenticate to SkySpark and send messages to the server which now using zinc format with a POST request. +****************************************************** +*/ //using .env plugin with composer, that's why I'm using this autoload file -//this is used for getting the uri, username, and password for skyspark login. -require_once '../../vendor/autoload.php'; class SkySpark { @@ -32,10 +35,10 @@ class SkySpark { private $password; private $serverUrl; - function __construct() { - $this->uri = getenv('SKYSPARK_URI'); //ex: "projUri/api/projName/eval?expr=" - $this->username = getenv('SKYSPARK_USERNAME'); //ex: username - $this->password = getenv('SKYSPARK_PASS'); //ex: password + function __construct($uri=null, $username=null, $password=null) { + $this->uri = $uri ?? getenv('SKYSPARK_URI'); //ex: "projUri/api/projName/eval" + $this->username = $username ?? getenv('SKYSPARK_USERNAME'); //ex: username + $this->password = $password ?? getenv('SKYSPARK_PASS'); //ex: password } @@ -45,151 +48,199 @@ function __construct() { * * *******************************************/ - /************************ - * Main SCRAM's function * - *************************/ - - function scram() { - - // the size in bytes of a SHA-256 hash - $dklen = 32; - - //SCRAM Autherntication Parameters - $serverUrl = 'https://skyspark.urlProjName.com/ui'; - - //Send url and username for first introduction in message 1 - $handshakeToken = $this->sendMsg1($serverUrl, $this->username); - - //Parse hanshakeToken from Server Response 1. - $handshakeToken = $this->get_string_between($handshakeToken, '=', ','); - - //Create a random but strong id. - $random = md5(uniqid(mt_rand(), true)); - - $clientNonce = $random; - - $clientFirstMsg = "n=".$this->username.",r=".$clientNonce; - - //Send url, Client's First Message, and the hansshakeToken in message 2 - $serverFirstMsg = $this->sendMsg2($serverUrl, $clientFirstMsg,$handshakeToken); - - //Parse Server Nonce, Server Salt, and Server Iterations from Server Response 2 - $serverNonce = $this->get_string_between($serverFirstMsg, 'r=', ','); - $serverSalt = $this->get_string_between($serverFirstMsg, 's=', ','); - $serverIterations = substr($serverFirstMsg, strpos($serverFirstMsg, "i=") + 2); - - //PBKDf2 for the SHA-256 hashing algorithm - $saltedPassword = hash_pbkdf2("sha256", $this->password, base64_decode($serverSalt), intval($serverIterations), $dklen, true); - - $gs2Header = base64_encode("n,,"); - $clientFinalNoPf = 'c='.$gs2Header.',r='.$serverNonce; - $authMessage = $clientFirstMsg.','.$serverFirstMsg.','.$clientFinalNoPf; - - //HMAC for SHA-256 hashing for the Client Key - $clientKey = hash_hmac('sha256',"Client Key", $saltedPassword, true); - - //hash the Stored Key - $storedKey = hash('sha256', $clientKey, true); - - //HMAC for SHA-256 hashing for the Client Signature - $clientSignature = hash_hmac('sha256', $authMessage, $storedKey, true); - - //Xor Client Key with Client Signature - $clientProof = ($clientKey ^ $clientSignature); - - $clientFinalMsg = $clientFinalNoPf.",p=".base64_encode($clientProof); - - //Send url, Client's Final Message, and the hansshakeToken in message 3 - $serverSecondMsg = $this->sendMsg3($serverUrl, $clientFinalMsg,$handshakeToken); - - return $serverSecondMsg; - - } - - /*********************** - * Message 1 Using cURL * - ************************/ - - function sendMsg1($serverUrl, $msg) { - - - $authMsg = "HELLO username=".rtrim(strtr(base64_encode($msg), '+/', '-_'), '='); - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, $serverUrl); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_HEADER , true); - curl_setopt($ch, CURLOPT_HTTPHEADER, array( - "Authorization: ". $authMsg, - "WWW-Authenticate: SCRAM" - )); - $serverMsg = curl_exec($ch); - - curl_close($ch); - - return $serverMsg; - } - - /*********************** - * Message 2 Using cURL * - ************************/ - - function sendMsg2($serverUrl, $msg, $handshakeToken) { - - $authMsg = "SCRAM handshakeToken=".$handshakeToken.", data=".rtrim(strtr(base64_encode($msg), '+/', '-_'), '='); - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, $serverUrl); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_HEADER , true); - curl_setopt($ch, CURLOPT_HTTPHEADER, array( - "Authorization: ". $authMsg, - "WWW-Authenticate: SCRAM" - )); - $serverMsg = curl_exec($ch); - $serverMsg = base64_decode($this->get_string_between($serverMsg,"data=",",")); - - curl_close($ch); - - return $serverMsg; - - } - - /*********************** - * Message 3 Using cURL * - ************************/ - - function sendMsg3($serverUrl, $msg,$handshakeToken) { - - $authMsg = "SCRAM handshakeToken=".$handshakeToken.", data=".rtrim(strtr(base64_encode($msg), '+/', '-_'), '='); - - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, $serverUrl); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_HEADER , true); - curl_setopt($ch, CURLOPT_FAILONERROR, false); - curl_setopt($ch, CURLOPT_HTTPHEADER, array( - "Authorization: " .$authMsg - )); - $serverMsg = curl_exec($ch); - $serverMsg = $this->get_string_between($serverMsg,"authToken=",","); - - curl_close($ch); - - return $serverMsg; - } - - /****************** - * Parse function * - *******************/ - - function get_string_between($string, $start, $end) { - - $string = ' ' . $string; - $ini = strpos($string, $start); - if ($ini == 0) return ''; - $ini += strlen($start); - $len = strpos($string, $end, $ini) - $ini; - return substr($string, $ini, $len); - } + protected function scram() { + // the size in bytes of a SHA-256 hash + $dklen = 32; + //SCRAM Autherntication Parameters + if($this->uri == null){ + throw new Exception("Skyspark Client not found"); + } + $serverUrl = explode('/api', $this->uri)[0]; + //Send url and username for first introduction in message 1 + $handshakeToken = $this->sendMsg1($serverUrl, $this->username); + //Parse hanshakeToken from Server Response 1. + $handshakeToken = $this->get_string_between($handshakeToken, '=', ','); + //Create a random but strong id. + $random = md5(uniqid(mt_rand(), true)); + $clientNonce = $random; + $clientFirstMsg = "n=".$this->username.",r=".$clientNonce; + //Send url, Client's First Message, and the hansshakeToken in message 2 + $serverFirstMsg = $this->sendMsg2($serverUrl, $clientFirstMsg,$handshakeToken); + //Parse Server Nonce, Server Salt, and Server Iterations from Server Response 2 + $serverNonce = $this->get_string_between($serverFirstMsg, 'r=', ','); + $serverSalt = $this->get_string_between($serverFirstMsg, 's=', ','); + $serverIterations = substr($serverFirstMsg, strpos($serverFirstMsg, "i=") + 2); + //PBKDf2 for the SHA-256 hashing algorithm + $saltedPassword = hash_pbkdf2("sha256", $this->password, base64_decode($serverSalt), intval($serverIterations), $dklen, true); + $gs2Header = base64_encode("n,,"); + $clientFinalNoPf = 'c='.$gs2Header.',r='.$serverNonce; + $authMessage = $clientFirstMsg.','.$serverFirstMsg.','.$clientFinalNoPf; + //HMAC for SHA-256 hashing for the Client Key + $clientKey = hash_hmac('sha256',"Client Key", $saltedPassword, true); + //hash the Stored Key + $storedKey = hash('sha256', $clientKey, true); + //HMAC for SHA-256 hashing for the Client Signature + $clientSignature = hash_hmac('sha256', $authMessage, $storedKey, true); + //Xor Client Key with Client Signature + $clientProof = ($clientKey ^ $clientSignature); + $clientFinalMsg = $clientFinalNoPf.",p=".base64_encode($clientProof); + //Send url, Client's Final Message, and the hansshakeToken in message 3 + $serverSecondMsg = $this->sendMsg3($serverUrl, $clientFinalMsg,$handshakeToken); + return $serverSecondMsg; + } + + /*********************** + * Message 1 Using cURL * + *************************/ + protected function sendMsg1($serverUrl, $msg) { + $authMsg = "HELLO username=".rtrim(strtr(base64_encode($msg), '+/', '-_'), '='); + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $serverUrl); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_HEADER , true); + curl_setopt($ch, CURLOPT_HTTPHEADER, array("Authorization: ". $authMsg,"WWW-Authenticate: SCRAM")); + $serverMsg = curl_exec($ch); + curl_close($ch); + return $serverMsg; + } + + /*********************** + * Message 2 Using cURL * + *************************/ + protected function sendMsg2($serverUrl, $msg, $handshakeToken) { + $authMsg = "SCRAM handshakeToken=".$handshakeToken.", data=".rtrim(strtr(base64_encode($msg), '+/', '-_'), '='); + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $serverUrl); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_HEADER , true); + curl_setopt($ch, CURLOPT_HTTPHEADER, array("Authorization: ". $authMsg,"WWW-Authenticate: SCRAM")); + $serverMsg = curl_exec($ch); + $serverMsg = base64_decode($this->get_string_between($serverMsg,"data=",",")); + curl_close($ch); + return $serverMsg; + } + + /*********************** + * Message 3 Using cURL * + *************************/ + protected function sendMsg3($serverUrl, $msg,$handshakeToken) { + $authMsg = "SCRAM handshakeToken=".$handshakeToken.", data=".rtrim(strtr(base64_encode($msg), '+/', '-_'), '='); + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $serverUrl); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_HEADER , true); + curl_setopt($ch, CURLOPT_FAILONERROR, false); + curl_setopt($ch, CURLOPT_HTTPHEADER, array("Authorization: " .$authMsg)); + $serverMsg = curl_exec($ch); + $serverMsg = $this->get_string_between($serverMsg,"authToken=",","); + curl_close($ch); + return $serverMsg; + } + + /****************** + * Parse function * + *******************/ + protected function get_string_between($string, $start, $end) { + $string = ' ' . $string; + $ini = strpos($string, $start); + if ($ini == 0) return ''; + $ini += strlen($start); + $len = strpos($string, $end, $ini) - $ini; + return substr($string, $ini, $len); + } + + protected function convertToZinc($data) { + // Convert Grid data to Zinc format + $zinc = "ver:\"3.0\"\n"; + + if (isset($data['cols']) && isset($data['rows'])) { + // Grid format + $colNames = []; + foreach ($data['cols'] as $col) { + $colNames[] = $col['name']; + } + $zinc .= implode(',', $colNames) . "\n"; + + foreach ($data['rows'] as $row) { + $zincRow = []; + foreach ($row as $value) { + $zincRow[] = '"' . $value . '"'; + } + $zinc .= implode(',', $zincRow) . "\n"; + } + } else { + // Simple format + foreach ($data as $key => $value) { + if ($key !== 'ver') { + $zinc .= $key . "\n\"" . $value . "\"\n"; + } + } + } + + return $zinc; + } + + /************************ + * Curl function that can now handle both GET and POST requests for current and deprecated SkySpark versions* + * Backward compatible with old 3-parameter signature: curl($authToken, $url, $debug) + *************************/ + protected function curl($authToken, $url, $debug=false, $method='GET', $postData=null) { + $ch = curl_init(); + $addr = $this->uri . $url; + curl_setopt($ch, CURLOPT_URL, $addr); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_HEADER , true); + curl_setopt($ch, CURLOPT_TIMEOUT, 30); // 30 second timeout + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); // 10 second connection timeout + + // Determine content type based on method and data + $contentType = "application/json"; // Default for GET requests + if ($method === 'POST' && $postData !== null) { + $contentType = "text/zinc"; // Use Zinc for POST with data + } + + curl_setopt($ch, CURLOPT_HTTPHEADER, array( + "Authorization: BEARER authToken=".$authToken, + "Accept: application/json", + "Content-type: " . $contentType + )); + + // Set request method + if ($method === 'POST') { + curl_setopt($ch, CURLOPT_POST, true); + if ($postData !== null) { + // Convert to Zinc format for SkySpark + $zincData = $this->convertToZinc($postData); + curl_setopt($ch, CURLOPT_POSTFIELDS, $zincData); + } + } elseif ($method !== 'GET') { + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); + if ($postData !== null) { + $jsonData = json_encode($postData); + curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonData); + } + } + // For GET requests, no additional curl options needed (default behavior) + + $serverMsg = curl_exec($ch); + + $str = strtok($serverMsg, "\n"); + $Get_header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE); + $yummy = array('":"r:', '":"s:', '":"n:','":"d:','":"t:'); + $Clean = str_replace($yummy,'":"', $serverMsg); + + $findAt = array('":"p:'); + $Clean2 = str_replace($findAt,'":"@p:', $Clean); + $Get_body = substr($Clean2, $Get_header_size); + if($debug) + { + return $Get_body; + } + + $Get_body_Array = json_decode($Get_body,true); + curl_close($ch); + return $Get_body_Array; + } } ?>