RSA Key and IV exchange failing, not found

Einklappen
X
 
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge
  • Bluebrain
    Smart Home'r
    • 18.10.2015
    • 77

    #16
    Noch so ein seltsames Verhalten, dass ich nicht nachvollziehen kann, warum Loxone das so unüblich gelöst hat:

    Ursprünglich hatte ich geplant, dass mein Script am Server, das die ganze Verschlüsselung macht, den Token abspeichert zusammen mit der validUntil Timestamp.
    Fordert der (Web/HTML/JavaScript) Client einen Token an zwecks Authentifizierung über Websocket, schaut der Server nach, ob der Token noch gültig ist und liefert diesen dann ggf. aus dem "Cache".
    Nur, wenn er nur noch <24 Stunden gültig ist, holt er sich einen neuen vom Miniserver.

    Das funktioniert aber nicht!
    Nachdem man sich einen Token geholt hat, muss man sich auch sofort über den Websocket authentifizieren und der Token bleibt dann nur für diese Verbindung gültig!
    Wird der Websocket geschlossen, kann man sich nach erneutem öffnen nicht erneut mit dem Token authentifiziern!

    Besonders idiotisch ist dieses Verhalten, wenn man keinen Websocket, sondern HTTP Requests mit Token-Authentifizierung nutzen möchte.
    Hier ist der Token nur für einen einzigen (Steuerungs-)Befehl gültig nach der Authentifizierung!

    Keine Ahnung, was sich Loxone dabei gedacht hat!
    Aber für HTTP Requests kann man sich diesen Mist mit dem Token sowieso sparen und ganz einfache HTTP Basic Auth nutzen.
    (z.B. "http://user:mypassword@192.168.0.21/dev/sps/io/VIBueroSpots/Impuls");
    Zuletzt geändert von Bluebrain; 01.01.2020, 16:28.

    Kommentar

    • Bluebrain
      Smart Home'r
      • 18.10.2015
      • 77

      #17
      Ich habe noch ein weiteres Script geschrieben, zur direkten Verwendung für Websockets:


      Das Script gibt JavaScript Code aus.
      in HTML dann einfach einbinden mit z.B.:
      Code:
      <script src="/cgi-bin/loxone_websocket_token.pl"></script>
      Das Script gibt folgenden JavaScript Code aus:
      Code:
      user='alexa';
      keyexchange_command='jdev/sys/keyexchange/UwENz/ilNVV2lpmCc1dmlqRyo0sArHMbxFbKCMtZhewXp1SzjVoJAgvj46tRwPxcNCpQqsM/01ZDjSGZDulm9cy+ihaJEKfNNBUap07J/UNvpdXjGvHkryQ5B0ZU9g/ZDSFLcdr+OjY2eU/N2kqnvKOmF80Y+5D8CKdXIh7FsA0=';
      token_valid=1;
      token_validUntil=1582730487;
      token_auth_command='jdev/sys/enc/xhyBSed98nDlg2XcJhHw%2F5VgfHlCVWa2aSXB%2BFVVFIV25f8klrazAs7dfjCc%2B9j131zk3Lzldui0OotUzQJXzwL0dlmcMjgJrerB3ZfK%2F8s%3D';
      In JavaScript dann einfach überprüfen, ob token_valid==1 und wenn ja, dann Websocket Verbindung zum Miniserver aufbauen.
      Dann keyexchange_command senden, und wenn das erfolgreich war (response code 200), dann noch token_auth_command senden.
      Damit sollte man dann authentifiziert sein und die Websocket Verbindung stehen.

      Nicht vergessen, regelmäßig, z.B. alle 60 Sek. ein "keepalive" an den Miniserver senden, damit die Websocket Verbindung nicht wegen Inaktivität geschlossen wird.
      (Einfach nur "keepalive" über den Websocket senden. Nicht /jdev/sys/keepalive oder ähnliches. Nur "keepalive"!)

      Kommentar

      • Bluebrain
        Smart Home'r
        • 18.10.2015
        • 77

        #18
        Weiter bin ich mit meinem Projekt auch noch nicht.

        Aber hier ein einfacher "quick and dirty" Client zum testen und herumspielen mit einer Websocket Verbindung zum Miniserver.
        Die UUIDs und dessen Werte werden auch berechnet und gespeichert. (nachdem man enablebinstatusupdate aktiviert/angefordert hat - Button ganz unten)

        Code:
        <html>
        <head>
            <title>Loxone Websocket Test</title>
            <style type="text/css">
            body, td {
                font-family: Verdana;
            }
            </style>
            <script src="/cgi-bin/loxone_token.pl"></script>
            <script>
            function e(objname) {
                return document.getElementById(objname);
            }
        
            function dt(msg) {
                e('dt').value+=msg+'\n';
                e('dt').scrollTop = e('dt').scrollHeight;
            }
        
            state=new Array();
            state[0]="CONNECTING";
            state[1]="OPEN";
            state[2]="CLOSING";
            state[3]="CLOSED";
        
            uuid = new Array();
        
            function display_socketstate() {
                e('state_text').innerHTML=state[connection.readyState];
            }
        
            function init() {
                if(token_valid) {
                    dt("keyexchange_command: "+keyexchange_command);
                    dt("token_auth_command: "+token_auth_command);
                    dt("token_validUntil: "+token_validUntil);
                    dt('');
                    connection = new WebSocket('ws://192.168.0.21/ws/rfc6455', ['remotecontrol']);
                    window.setInterval("display_socketstate()",1000);
                } else {
                    dt("invalid token, cannot proceed");
                }
        
                connection.onopen = function () {
                    console.log("Websocket connection to Miniserver established");
                    connection.send(keyexchange_command);
                };
        
                // Log errors
                connection.onerror = function (error) {
                    console.log('WebSocket Error ' + error);
                };
        
                connection.onclose = function(event) {
                    if (event.wasClean) {
                        dt(`[close] socket closed cleanly, code=${event.code} reason=${event.reason}`);
                    } else {
                        dt('[close] socket died');
                    }
                };
        
                identifier_text = [
                    "Text-Message",
                    "Binary File",
                    "Event-Table of Value-States",
                    "Event-Table of Text-States",
                    "Event-Table of Daytimer-States",
                    "Out-Of-Service Indicator",
                    "Keepalive response",
                    "Event-Table of Weather-States",
                    "MESSAGE HEADER"
                ];
        
                connection.binaryType = 'arraybuffer';
                auth_done=0;
                next_message_type=8;
                connection.onmessage = function (e) {
                    //console.log(e.data);
                    dt("\n#incoming: "+typeof e.data);
                    if(typeof e.data === "object") {
                        dt("Received arraybuffer");
                        dt("length="+e.data.byteLength);
                        var buffer = e.data;
                        var m = new Uint8Array(buffer,0,4);
                        dt(m[0]+' '+m[1]+' '+m[2]+' '+m[3]);
                        if(m[0]==3 && (m[2]==0 || m[2]==128) && e.data.byteLength==8) {
                            dt("MESSAGE HEADER");
                            next_message_type=m[1];
                            dt("next message: "+identifier_text[next_message_type]);
                            var view = new DataView(buffer,4,4);
                            msglength=0;
                            msglength = view.getUint32(0,true);
                            if(m[2]!=0) {
                                dt("estimated length: "+msglength);
                            } else {
                                dt("actual length: "+msglength);
                            }
                        } else {
                            if(next_message_type==2) {
                                dt("CALCULATING VALUE STATES");
                                var items = e.data.byteLength/24;
                                for(var i=0; i<items; i++) {
                                    var pos=i*24;
                                    // uuid: 0d159ae3-0033-3c2b-ffff4252598e40a7 value: 22.5
                                    // 11223344556677889900112233445566
                                    // 0d159ae300333c2bffff4252598e40a7
                                    var view = new DataView(buffer,pos+0,4);
                                    data1 = view.getUint32(0,true);
                                    var view = new DataView(buffer,pos+4,2);
                                    data2 = view.getUint16(0,true);
                                    var view = new DataView(buffer,pos+6,2);
                                    data3 = view.getUint16(0,true);
                                    data4 = new Uint8Array(buffer,pos+8,8);
        
                                    var uuid_name = ("00000000" + data1.toString(16)).substr(-8);
                                    uuid_name += '-';
                                    uuid_name += ("0000" + data2.toString(16)).substr(-4);
                                    uuid_name += '-';
                                    uuid_name += ("0000" + data3.toString(16)).substr(-4);
                                    uuid_name += '-';
                                    uuid_name += ("00" + data4[0].toString(16)).substr(-2);
                                    uuid_name += ("00" + data4[1].toString(16)).substr(-2);
                                    uuid_name += ("00" + data4[2].toString(16)).substr(-2);
                                    uuid_name += ("00" + data4[3].toString(16)).substr(-2);
                                    uuid_name += ("00" + data4[4].toString(16)).substr(-2);
                                    uuid_name += ("00" + data4[5].toString(16)).substr(-2);
                                    uuid_name += ("00" + data4[6].toString(16)).substr(-2);
                                    uuid_name += ("00" + data4[7].toString(16)).substr(-2);
        
                                    var view = new DataView(buffer,pos+16,8);
                                    dval = view.getFloat64(0,true);
        
                                    uuid[uuid_name]=dval;
        
                                    dt("uuid: "+uuid_name+" -> "+uuid[uuid_name]);
                                }
                            }
                        }
                    } else {
                        if(next_message_type==0) {
                            var dm = e.data + '';
                            dm = dm.replace('\n','');
                            dt(dm);
                            var j = JSON.parse(e.data);
                            code = j.LL.Code;
                            if(code==undefined) {
                                code = j.LL.code;
                            }
                            dt('code: '+code+'\n');
        
                            if(code==200 && !auth_done) {
                                auth_done=1;
                                connection.send(token_auth_command);
                            }
                        }
                    }
                };
            }
        
            function send_to_socket(msg) {
                dt("sending: "+msg);
                connection.send(msg);
            }
            </script>
        </head>
        <body onLoad="init();">
        
        <h1>Loxone Websocket Test</h1>
        <input type="text" style="width:1070px; font-size:16px; font-family:Courier New;" id="to_send"> <input type="button" value="send" onClick="send_to_socket(e('to_send').value);"><br>
        <br>
        <textarea id="dt" cols=150 rows=50></textarea><br>
        <input type="button" value="close socket" onClick="connection.close();">
        socket state: <span id="state_text">CLOSED</span><br>
        <br>
        <input type="button" value="enablebinstatusupdate" onClick="send_to_socket('jdev/sps/enablebinstatusupdate');">
        </body>

        Kommentar

        • Gast

          #19
          Hallo zusammen, falls jmd. interesse an einem kommentierten Python Script hat, dass sich an der (echt nicht übersichtlichen) Doku von Loxone zur Kommunikation mit dem Miniserver entlanghangelt, bitteschön: https://github.com/alxgross/Loxone_Websockets_Demo

          Kommentar

          Lädt...