Utilisation de "HTTP authorization Digest" dans un script

Discussion et échanges de scripts pour la box eedomus

Utilisation de "HTTP authorization Digest" dans un script

Messagepar clio_Td » 05 Avr 2022 14:22

Bonjour,

est ce que quelqu'un a déjà utilisé "HTTP authorization Digest" dans un script eedomus ?

Le but est de commander les TV Philips d'après 2016.

Si quelqu'un l'a déjà fait peut on avoir un exemple de code.

Si personne de l'a fait et que l'équipe eedomus passe, pouvez vous nous expliquer ou implémenter cette fonctionnalité ?

Merci.
clio_Td
 
Messages : 313
Inscription : 10 Avr 2012

Re: Utilisation de "HTTP authorization Digest" dans un scrip

Messagepar dommarion » 06 Avr 2022 10:46

clio_Td a écrit:Bonjour,
est-ce que quelqu'un a déjà utilisé "HTTP authorization Digest" dans un script eedomus ?
Le but est de commander les TV Philips d'après 2016.
Si quelqu'un l'a déjà fait peut-on avoir un exemple de code.
Si personne de l'a fait et que l'équipe eedomus passe, pouvez-vous nous expliquer ou implémenter cette fonctionnalité ?
Merci.

Bonjour clio_td,
Merci pour ce post dans le forum, cela va nous aider en effet à avancer sur un Plugin TV Philips.
De mon côté, j'ai développé en PHP un bout de script qui permet de construire le header HTTP authorization Digest en m'inspirant des différents exemples trouvés sur GitHub.
Je dois trouver comment lire le Header envoyé par la TV Philips de type (HTTP/1.1 401 Authorization Required WWW-Authenticate: Digest realm="Digest Realm", nonce="uD85Pg==a766f996fa716e4d4592943b5762c73958f0378b", opaque="5ccc069c403ebaf9f0171e9517f40e41", algorithm=MD5, domain="/digest", qop="auth").
Je dois récupérer dans ce header: realm, nonce, opaque, algorithm, qop et domain.
J'ai essayé $_SERVER sans succès pour l'instant.

Si quelqu'un peut aider sur ce sujet... :geek: :ugeek:

Merci pour vos contributions.
Bonne journée
dommarion
OpenWeather|Phases soleil|HeatzyV2|Concaténateur|HP Yamaha|SomfyV3|Epson|Seuils n étages|Baie 2 vantaux|Deezer|Intégrale|Authorization Digest|TV Philips|SmartThings|fonction PHP|Tuya Smartlife|CozyTouch2|mySMS
dommarion
 
Messages : 588
Inscription : 28 Déc 2020

Re: Utilisation de "HTTP authorization Digest" dans un scrip

Messagepar dommarion » 06 Avr 2022 16:50

dommarion a écrit:Je dois trouver comment lire le Header envoyé par la TV Philips de type (HTTP/1.1 401 Authorization Required WWW-Authenticate: Digest realm="Digest Realm", nonce="uD85Pg==a766f996fa716e4d4592943b5762c73958f0378b", opaque="5ccc069c403ebaf9f0171e9517f40e41", algorithm=MD5, domain="/digest", qop="auth").
Je dois récupérer dans ce header: realm, nonce, opaque, algorithm, qop et domain.

Voilà j'ai trouvé, avec l'aide de clio_Td. :P
Il faut utiliser httpQuery de la box eedomus
L'argument $info permet de récupérer un tableau contenant entre autre le code HTTP, le header, ...
httQuery($url, 'POST', $post , NULL, $headers,false,false, &$info, NULL)
$info["header"] contient la réponse de la TV
"HTTP/1.1 401 Unauthorized
Date: Wed, 06 Apr 2022 15:32:32 GMT
Accept-Ranges: bytes
Server: Restlet-Framework/2.3.12
WWW-Authenticate: Digest realm="XTV", domain="/", nonce="MTY0OTI1OTE1MjYwNjpmNGQ5MTBmZDViOTg3ZDc5OTlkYzFiNjdjZWZhNzQ3Mw==", algorithm=MD5, qop="auth"
Content-Length: 424
Content-Type: text/html; charset=UTF-8"
Une étape de passée... :ugeek:
Bonne soirée
dommarion
OpenWeather|Phases soleil|HeatzyV2|Concaténateur|HP Yamaha|SomfyV3|Epson|Seuils n étages|Baie 2 vantaux|Deezer|Intégrale|Authorization Digest|TV Philips|SmartThings|fonction PHP|Tuya Smartlife|CozyTouch2|mySMS
dommarion
 
Messages : 588
Inscription : 28 Déc 2020

Re: Utilisation de "HTTP authorization Digest" dans un scrip

Messagepar dommarion » 06 Avr 2022 16:51

Le nouveau Header est calculé selon la spec rfc7616 (pas évident :roll: )
Maintenant il reste la syntaxe du Header de réponse à bien comprendre pour renvoyer la requête Authorization Digest... :oops:
Dernière édition par dommarion le 08 Avr 2022 11:31, édité 2 fois.
OpenWeather|Phases soleil|HeatzyV2|Concaténateur|HP Yamaha|SomfyV3|Epson|Seuils n étages|Baie 2 vantaux|Deezer|Intégrale|Authorization Digest|TV Philips|SmartThings|fonction PHP|Tuya Smartlife|CozyTouch2|mySMS
dommarion
 
Messages : 588
Inscription : 28 Déc 2020

Re: Utilisation de "HTTP authorization Digest" dans un scrip

Messagepar ARTNOW » 06 Avr 2022 17:20

top ;)
EEDOMUS + -RFP1000-IPX 800 V4-IPX 800 V5 - FIBARO -GOOGLE(Home-Nest & mini)
ARDUINO-RASPBERRY PRUSA MK3S
Domotisation de :1xPiscine/9xClimatisations réversibles/2xVolets somfy/1xAlarme/1xChauffe-Eau thermodynamique/3xPortes Garage et 1xportail
ARTNOW
 
Messages : 1282
Inscription : 22 Avr 2018
Localisation : LOIRE ATLANTIQUE

Re: Utilisation de "HTTP authorization Digest" dans un scrip

Messagepar dommarion » 09 Avr 2022 09:15

Bonjour,
Voilà l'étape passée sur le HTTP authorization Digest. :ugeek:
L'étape d'authentification se compose en une demande non Digest, qui obtient une réponse (dans le header) de type Digest avec les informations minimales realm, nonce et qop, d'autres informations peuvent être fournies. Sans ces informations minimales, il est impossible de communiquer entre serveur et client en mode Digest.
La phrase réponse Digest est élaborée avec des calculs de cryptage selon la spécification rfc7616. Pour élaborer cette réponse, des éléments doivent être rajoutés tels que username, password, nc, cnonce, uri, method, d'autre éléments peuvent être ajoutés en option.
Example Authentification Digest.png
Exemple de communication Authorization Digest entre un serveur et vous (comme client)
Example Authentification Digest.png (50.93 Kio) Consulté 4309 fois

Cette réponse sera insérée dans le header de la réponse à la rubrique Authorization, avec d'autres éléments plus classiques d'un header de type Host, Accept, Content-Length, Content-Type.
Le requête httpQuery() peut alors être envoyée avec les paramètres $url, $action = 'GET' ou 'POST', $post = NULL, $oauth_token = NULL, $headers, $use_cookies = false, $ignore_errors = false, &$info, $user_pwd = NULL).
En réponse on obtient une confirmation d'accord et dans le header de la réponse HTTP 200 OK.
Attention: Il est important d'une part de gérer l'incrémentation de nc (compteur) à chaque requête envoyée et réussie, et d'autre part de toujours envoyer "telle quelle" la variable nounce. Sinon les requêtes ne seront pas acceptées.
Il y a beaucoup de documentation à ce sujet sur internet, j'ai utilisé principalement celle-ci: https://httpwg.org/specs/rfc7616.html
Le script PHP (compatible eedomus ;) ) est disponible pour ceux qui veulent intégrer dans leur PLUGIN une communication de type Authorization Digest avec un objet connecté ou un site.
Bonne journée à toutes et tous.
dommarion
Dernière édition par dommarion le 09 Avr 2022 15:28, édité 1 fois.
OpenWeather|Phases soleil|HeatzyV2|Concaténateur|HP Yamaha|SomfyV3|Epson|Seuils n étages|Baie 2 vantaux|Deezer|Intégrale|Authorization Digest|TV Philips|SmartThings|fonction PHP|Tuya Smartlife|CozyTouch2|mySMS
dommarion
 
Messages : 588
Inscription : 28 Déc 2020

Re: Utilisation de "HTTP authorization Digest" dans un scrip

Messagepar klaushomelive » 09 Avr 2022 10:58

C'est du travail perfekt !
Pièces jointes
tirer-le-chapeau.jpg
tirer-le-chapeau.jpg (862 octet(s)) Consulté 4302 fois
klaushomelive
 
Messages : 144
Inscription : 28 Avr 2019

Re: Utilisation de "HTTP authorization Digest" dans un scrip

Messagepar dommarion » 15 Avr 2022 16:55

Bonjour,
Vous trouvrez le script correspondant à la fonction qui gère le Header Digest pour une communication de type HTTP/HTTPS Authorization Digest.
D'autres fonctions ont été ajoutées, elle peuvent être utiles dans le script principal mais pas nécessaires dans le gestion du Header Digest.
Vos commentaires sont la bienvenue pour améliorer cette boîte à outils.
Bon weekend de Pâques.
dommarion
Code : Tout sélectionner
<?php
//------------------------------------------------------------------------------------------------------------------------------------------------------
// Version 1.0 dommarion - PHP developped function for Handelling an HTTP/HTTPS with the Digest Authorization protocol based on RFC 7616 specification
// Reference at this adress: https://httpwg.org/specs/rfc7616.html
// Specific for eedomus box - less than de 50Ko and limited PHP instructions
//
// Global variables (to be definced and managed into the main script)
// $debug : set by default to 0 and set to 1 to get debuggging trace by echo and var_dump functions
// $ncvalue : the counting of successful Digest communications to be handled after a successful httpQuery by checking the header value "HTTP/1.1 200 OK"
// $uniqId : a unique id to identify the saved variable if the Plugin is used on more than one instance (usually the
//
// Input data
// Mandatory input data:
// $username : username for the Digest authentication
// $password : password for the Digest authentication
//
// Optional or set by default input data
// $method='GET' : values can be 'GET" or 'POST'
// $request_uri=null : URI to address during the 'POST' query
// $payload=null : Body message to send during the 'POST' query, needed only if qop='auth-int' and 'POST' query
// $first_server_header=null : first header received when requesting an authentication, this header should at least contain realm and nonce values
//------------------------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------
// Toolbox functions - not used in this specific function, but useful in the main script
//---------------------------------------------------------------------------------------

// Specific eedomus function to replace the PHP json_encode()
function sdk_json_encode($value){
    if (is_string($value)) return '"'.addslashes($value).'"';
    if (is_numeric($value)) return $value;
    if ($value === null) return 'null';
    if ($value === true) return 'true';
    if ($value === false) return 'false';

    $assoc = false;
    $i = 0;
    foreach ($value as $k=>$v){
        if ($k !== $i++){
            $assoc = true;
            break;
        }
    }
    $res = array();
    foreach ($value as $k=>$v){
        $v = sdk_json_encode($v);
        if ($assoc){
            $k = '"'.addslashes($k).'"';
            $v = $k.':'.$v;
        }
        $res[] = $v;
    }
    $res = implode(',', $res);
    if ($assoc) {return '{'.$res.'}';}
    else {return '['.$res.']';}
}

// Random character to generate a String
function sdk_generateRandomString($length = 16) {
   global $debug;
   if (is_int($length))
      {
      if ($length >0)
         {
         $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
             $charactersLength = strlen($characters);
             $randomString = '';
             for ($i = 0; $i < $length; $i++)
            {
                 $randomString .= $characters[rand(0, $charactersLength - 1)];
                }
         }
      else {$randomString='';}
      }
   else {$randomString='';}
    return $randomString;
}

// Specific eedomus function to replace the PHP strstr()
function sdk_strstr($string,$needle,$before=false) {
   $found=strpos($string,$needle);
   if (is_int($found)) {
      if ($before) {$reply=substr($string,0,$found);}
      else {$reply=substr($string,$found,strlen($string)-$found);}
   }
   else {$reply= false;}
   return $reply;
}

// Specific eedomus function to replace the PHP array_search()
function sdk_array_search($element,$array) {
   global $debug;
   $length=count($array);
   $i=0;
   $key=null;
   for ($i;$i<$length;$i++) {
      if ($element == $array[$i]) {$key=$i;}
   }
   return $key;
}

// Specific eedomus function to replace the PHP base64_decode()
function sdk_base64_decode($encode64_string) {
   global $debug;
   $base=array('A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/');
   $array_string=str_split($encode64_string, 1);
   $rank=array();
   $i=0;
   $binary="";
   foreach ($array_string as $id=>$letter)
       {if ($letter!="=")
           {$rank[$i]=sdk_array_search($letter,$base);
           $binary=$binary.str_pad(decbin($rank[$i]), 6, '0', STR_PAD_LEFT);
           $i=$i+1;
           }
       }
   $length=strlen($binary);
   $integer=intval($length/8);
   $eightbit=array();
   $j=0;
   $decode64='';
   for ($j=0; $j<$integer; $j++)
       {$eightbit[$j]=substr($binary, $j*8, 8);
       $decode64.= chr(intval($eightbit[$j], 2));
       }
   return $decode64;
}

// Function to generate a sha1 coding signature using hmac based on RFC 2104
function sdk_create_signature($the_key, $to_sign) {
   global $debug;
   $signature = hash_hmac("sha1", utf8_encode($to_sign), $the_key);
   $signature = base64_encode(utf8_encode($signature));
   return $signature;
}
//
//---------------------------------------------------------------------------------------
// Functions used in this part
//---------------------------------------------------------------------------------------

// Function to hash using md5 or sha1 or sha256 or sha512 or haval160 or ...
function sdk_H($algorithmm,$parameter) {
        return hash($algorithmm,$parameter,false);
}

// Function to hash a merged result of a:b
function sdk_KD($algorithmm,$a,$b) {
   return sdk_H($algorithmm,"$a:$b");
}

// Function to transform an Header in an Array
function sdk_getHeaders($respHeaders) {
   global $debug;
// If missing line end then add it before processing it
   if (!strpos($respHeaders, "\r\n\r\n")) {$respHeaders .=  "\r\n\r\n";}
   $headers = array();
   $headerText = substr($respHeaders, 0, strpos($respHeaders, "\r\n\r\n"));
   foreach (explode("\r\n", $headerText) as $i => $line) {
           if ($i === 0) {
                  $headers['http_code'] = $line;
              }
      else {
                  list ($key, $value) = explode(': ', $line);
                  $headers[$key] = $value;
              }
          }
    return $headers;
}

// Function to parse the WWW-Authenticate: Digest part of the Header
function sdk_parsehttpdigest($digest) {
   global $debug;
   $data = array();
   $parts = explode(", ", $digest);
        $i=0;
   foreach ($parts as $element) {
        $length=strlen($element);
        $bits=array();
        for ($j=0; $j<$length; $j++)
            {
           $char=substr($element, $j, 1);
           if ($char == '=')
            {$bits[0] = substr($element,0,$j);
            $bits[1] = substr($element,$j+1,$length-($j+1));
            $j=$length;}
            }
          if (isset($bits[1]))
             {$data[$bits[0]] = str_replace('"','', $bits[1]);}
          else
             {$data[$bits[0]] = str_replace('"','', "");}
   
       $i=$i+1;
   }
   return $data;
}

// Function to decode, calculate and prepare the Header Digest to be sent to the server
function sdk_httpdigestauth($username, $password, $method='GET', $request_uri=null, $payload=null, $first_server_header=null) {       
   global $debug,$ncvalue,$uniqId;
   if ($first_server_header !== null) {
      $headers=sdk_getHeaders($first_server_header);
      list($dummy_digest,$value) = explode('Digest ', $headers['WWW-Authenticate'], 2);
// Parsing the Digest Header part to allocate the needed parameters
           $x = sdk_parsehttpdigest($value);
// if the parameter: "realm" has not been found into the first_header - MANDATORY
      if (!isset($x['realm'])) {$realm=null;}
           else {$realm = $x['realm'];}
// if the parameter: "domain" has not been found into the first_header - IGNORED
      if (!isset($x['domain'])) {$domain=null;}
           else {$domain=$x['domain'];}
// if the parameter: "nonce" has not been found into the first_header - MANDATORY
      if (!isset($x['nonce'])) {$nonce=null;}
           else {$nonce=$x['nonce'];}
// if the parameter: "opaque" has not been found into the first_header - OPTIONAL
      if (!isset($x['opaque'])) {$opaque=null;}
           else {$opaque = $x['opaque'];}     
// if the parameter: "stale" has not been found into the first_header - OPTIONAL
      if (!isset($x['stale'])) {$stale=null;}
           else {$stale = $x['stale'];}     
// if the parameter: "algorithm" has not been found into the first_header - OPTIONAL by default = MD5
      if (!isset($x['algorithm'])) {$algorithm="MD5";}
           else {$algorithm=$x['algorithm'];}
// if the parameter: "qop" has not been found into the first_header - MANDATORY by default = auth
      if (!isset($x['qop'])) {$qop="auth";}
           else {$qop=$x['qop'];}
// if the parameter: "charset" has not been found into the first_header - OPTIONAL
      if (!isset($x['charset'])) {$charset=null;}
           else {$charset=$x['charset'];}
// if the parameter: "userhash" has not been found into the first_header - OPTIONAL
      if (!isset($x['userhash'])) {$userhash=null;}
           else {$userhash = $x['userhash'];}     
// if the parameter: "nc" has not been found into the first_header - OPTIONAL
      if (!isset($x['nc'])) {$ncvalue=null;}
      else {$ncvalue=$x['nc'];}
      }
   else   {
// In case no more first_header, mandatory parameters are retreived from saved data for further communication
      $realm = loadVariable('realm'.$uniqId);
      $nonce = loadVariable('nonce'.$uniqId);
      $qop = loadVariable('qop'.$uniqId);
      $ncvalue = loadVariable('ncvalue'.$uniqId);
// if the parameter: "qop" has not been restored from saved data - MANDATORY by default = auth
      if (!isset($qop)) {$qop="auth";}
      $algorithm = loadVariable('algorithm'.$uniqId);
// if the parameter: "algorithm" has not been restored from saved data - OPTIONAL by default = MD5
      if (!isset($algorithm)) {$qop="MD5";}
      }
// nc: nouce count is starting with 1 and increased by 1 at each successful communication between client and servor nc=string(8) "00000001"
   if (isset($ncvalue)) {$ncvalue=dechex(hexdec($ncvalue)+1);}
   else {$ncvalue=1;}
   if (hexdec($ncvalue) >= hexdec('100000000'))
      {$ncvalue=1;
      }
// Formattage du nc avec plusieurs 0 avant le nombre entier
   $nc = str_pad($ncvalue, 8, '0', STR_PAD_LEFT);
// If userhash=true then username is encryped using KD function with $realm based on the specified algorithm
   if (isset($userhash)&&$userhash) {$username=sdk_KD($algorithm,$username,$realm);}
// Computation of A1, A2, cnonce according to RFC 7616 specification
        $A1 = $username.":".$realm.":".$password;       
        $A2 = $method.":".$request_uri;
        $cnonce = time();
   if ($qop == 'auth-int')
      {
// In case of 'auth-int' -> the Body message will be included into the A2 parameter according to RFC 7616 specification
           $A2 = $method.":".$request_uri.":".sdk_H($algorithm,$payload);
      }
        $noncebit = $nonce.":".$nc.":".$cnonce.":".$qop.":".sdk_H($algorithm,$A2);
// Computation of the Digest response parameter according to RFC 7616 specification
        $respdig = sdk_KD($algorithm,sdk_H($algorithm,$A1), $noncebit);
//The value of the header field can include parameters list: username, realm, nonce, uri, qop, nc, cnonce, response,  opaque, userhash and algorithm
        $header_Digest  = 'username="'.$username.'", realm="'.$realm.'", nonce="'.$nonce.'", uri="'.$request_uri.'", qop="'.$qop.'", nc="'.$nc.'", cnonce="'.$cnonce.'", response="'.$respdig.'"';
        if (isset($opaque)) {$header_Digest .= ', opaque="'.$opaque.'"';}
        if (isset($userhash)) {$header_Digest .= ', userhash='.$userhash;}
        $header_Digest .= ', algorithm='.$algorithm;
// Debug Mode to trace and display
   if ($debug == 1)
      {echo "Parameters:\n";
      echo "pass=".$password."\n";
      echo "method=".$method."\n";
      echo "A1=".$A1."\n";
      echo "A2=".$A2."\n";
      echo "noncebit=".$noncebit."\n";
      echo $header_Digest;
      }
// Saving Parameters for further use: realm, nonce, qop, algorithm - ncvalue will be backed-up only when the Digest communication will be acepted by the server with the HTTP/1.1 200 OK
      saveVariable('realm'.$uniqId,$realm);
      saveVariable('nonce'.$uniqId,$nonce);
      saveVariable('qop'.$uniqId,$qop);
      saveVariable('algorithm'.$uniqId,$algorithm);
        return $header_Digest;
}

//---------------------------------------------------------------------------------------
/* example of a function GET with httpQuery
// Function httpQuery 'GET' with authentification Digest - parameter $url=string,$user=string,$pass=string,$uri=string, return $state=array()
function sdk_GETDigest($url,$user,$pass,$uri){
   global $debug, $host, $port, $ncvalue, $uniqId;
   $method='GET';
   $ignore_errors = true;
   $payload="";
   $header_received=array();
   $headerDigest=sdk_httpdigestauth($user, $pass, $method, $uri, $payload=null, $server_header=null);
   $header=array("Host: ".$host.":".$port,"Accept: application/json;charset=utf-8","Authorization: Digest ".$headerDigest,"Content-Length:".strlen($payload),"Content-Type:application/x-www-form-urlencoded;charset=utf-8");
   $reply=httpQuery($url, $method, null, null, $header, false, $ignore_errors,&$header_received,null);
   if (sdk_strstr($header_received['header'],"HTTP/1.1 200 OK"))
      {saveVariable('ncvalue'.$uniqId,$ncvalue);}
// Debug Mode to trace and display
   if ($debug == 1) {echo "Header response:";var_dump($header_received['header']);}
   return $reply;
}
*/
/* example of a function POST with httpQuery
// Function httpQuery 'POST' with authentification Digest - parameter $url=string,$payload=string,$user=string,$pass=string,$uri=string return $state=array()
function sdk_POSTDigest($url,$payload,$user,$pass,$uri) {
   global $debug, $host, $port, $ncvalue, $uniqId;
   $method='POST';
   $ignore_errors=true;
   $header_received=array();
   $headerDigest=sdk_httpdigestauth($user, $pass, $method, $uri, $payload=null, $server_header=null);
   $header=array("Host: ".$host.":".$port,"Accept: application/json;charset=utf-8","Authorization: Digest ".$headerDigest,"Content-Length:".strlen($payload),"Content-Type:application/x-www-form-urlencoded;charset=utf-8");
   $reply=httpQuery($url,$method, $payload, null, $header,false,$ignore_errors,&$header_received,null);
// Debug Mode to trace and display
   if ($debug == 1) {echo "Header response:";var_dump($header_received['header']);}
   if (sdk_strstr($header_received['header'],"HTTP/1.1 200 OK"))
      {saveVariable('ncvalue'.$uniqId,$ncvalue);}
   return $reply;
}
*/

?>
OpenWeather|Phases soleil|HeatzyV2|Concaténateur|HP Yamaha|SomfyV3|Epson|Seuils n étages|Baie 2 vantaux|Deezer|Intégrale|Authorization Digest|TV Philips|SmartThings|fonction PHP|Tuya Smartlife|CozyTouch2|mySMS
dommarion
 
Messages : 588
Inscription : 28 Déc 2020


Retour vers Scripts & Périphériques du store

Qui est en ligne ?

Utilisateurs parcourant ce forum : Aucun utilisateur inscrit et 54 invité(s)