Quick Start

Last Updated on : 2024-04-08 10:27:04download

This topic describes how to enable LAN communication and authorize a third-party application to access the gateway.

Step 1: Enable LAN communication

LAN communication is disabled by default. It can be enabled on the mobile app.

  1. Tap Advanced Features on the app panel.
  2. Select LAN Control and tap the toggle to enable or disable this feature.

After LAN communication is enabled, the app panel will show the device key used to encrypt data for identity authentication. Be sure to keep the device key safe.

Step 2: Implement authentication

The WebSocket protocol is used for LAN communication, with data encrypted using the AES-128-ECB algorithm. After establishing a WebSocket connection, both parties will authenticate each other’s identity before communication occurs. The data is encrypted using the session key negotiated during authentication.

Authentication process

  1. The gateway generates a 16-byte random number nonce S and encodes it in base64 format.

  2. The gateway sends the base64-encoded nonce S to the third-party application in a message of auth_required type.

  3. The third-party application receives the data, decodes nonce S using base64, calculates the HMAC with SHA-256, and encodes it in base64.

  4. The third-party application generates a 16-byte random number nonce C and encodes it in base64 format.

  5. The third-party application sends the base64-encoded nonce C and HMAC to the gateway in a message of auth type.

  6. The gateway receives the data, decodes nonce C using base64, calculates the HMAC with SHA-256, and encodes it in base64.

  7. The gateway checks the HMAC from the third-party application. If verified, it will send the base64-encoded HMAC to the third-party application in a message of auth_ok type.

  8. The third-party application checks the HMAC from the gateway. If verification fails, the connection will be terminated.

    The data is encrypted using the AES-128-ECB algorithm for transmission, with the key displayed on the app panel.

GatewayThird-Party ApplicationGenerate a random number nonce S.type: auth_required, data: base64(nonce S)Generate a random number nonce C.type: auth, data: base64(nonce C), base64(SHA-256(nonce S))Authenticate identity.type: auth_ok, data: base64(SHA-256(nonce C))Close WebSocket connection.alt[Successful][Failed]Authenticate identity.Close WebSocket connection.alt[Failed]GatewayThird-Party Application

Session key exchange

  1. The gateway generates the session key by performing an XOR operation on nonce S and nonce C.
  2. The third-party application generates the session key by performing an XOR operation on nonce S and nonce C.
  3. The communication data will be encrypted and decrypted with the session key.
GatewayThird-Party ApplicationSession Key = nonce C ^ nonce SSession Key = nonce C ^ nonce SData encrypted with session key.Data encrypted with session key.GatewayThird-Party Application

Step 3: Example

In this Python example, a WebSocket client connects to a gateway over LAN, performs identity authentication, and gets the list of devices connected to the gateway.

In the example, replace YOUR_IP_ADDRESS with your gateway’s IP address and YOUR_SECRET_KEY with your gateway key (the key shown on the app panel).

Example:

import websocket
import json
import os

from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import hashlib
import hmac
import base64

class LocalApi(object):
    def __init__(self, url: str, key: bytes):
        self.nonce_c = os.urandom(16)
        self.nonce_s = None
        self.key = key
        self.url = url
        self.ws = websocket.WebSocket()
        self.cipher = AES.new(self.key, AES.MODE_ECB)

        self.ws.connect(self.url)

    def enc_msg(self, msg: bytes) -> bytes:
        padded_msg = pad(msg, AES.block_size)
        return self.cipher.encrypt(padded_msg)

    def dec_msg(self, msg: bytes) -> bytes:
        return unpad(self.cipher.decrypt(msg), AES.block_size)

    def _auth_required(self, data):
        self.nonce_s = base64.b64decode(data['data']['nonce'])

        hmac_sha256 = hmac.new(self.key, self.nonce_s, hashlib.sha256)
        hmac_hex = hmac_sha256.digest()
        hmac_str = base64.b64encode(hmac_hex).decode('utf-8')

        msg = json.dumps({
            "type": "auth",
            "data": {
                "nonce": base64.b64encode(self.nonce_c).decode('utf-8'),
                "hmac": hmac_str
            }
        })

        self.ws.send(self.enc_msg(msg.encode()))

    def _auth_ok(self, data):
        self.key = bytes([a ^ b for a, b in zip(self.nonce_c, self.nonce_s)])
        self.cipher = AES.new(self.key, AES.MODE_ECB)
        self._get_devices()

    def _get_devices(self):
        msg = json.dumps({
            "id": 1,
            "type": "get_devices"
        })

        self.ws.send(self.enc_msg(msg.encode()))

    def run(self):
        while True:
            ws_data = self.ws.recv()
            data = json.loads(self.dec_msg(ws_data))
            print(f"recv data: {data}")
            if data['type'] == 'auth_required':
                self._auth_required(data)
            elif data['type'] == 'auth_ok':
                self._auth_ok(data)
            elif data['type'] == 'event':
                pass

        self.ws.close()

url = "ws://<YOUR_IP_ADDRESS>:8080"
key = b'<YOUR_SECRET_KEY>'

client = LocalApi(url, key)
client.run()

Log:

recv data: {'type': 'auth_required', 'data': {'nonce': 'nQ+UcvlKWSbPSoXic1o1Tg=='}}
recv data: {'type': 'auth_ok', 'data': {'hmac': '5uulS0qaM1aSE5ISvE4puEJ2rZ3kK1YIW/1yw+szTVw='}}
recv data: {'id': 1, 'type': 'result', 'success': True, 'result': ['000d6ffffe67e2ca-1', '000d6ffffe67e2ca']}