Implement new authentication commands and improve PSK handling
This commit is contained in:
+82
-12
@@ -352,24 +352,26 @@ func (c *Client) doKAuth() error {
|
|||||||
fmt.Printf("[Wyze] K10001 received, status=%d\n", status)
|
fmt.Printf("[Wyze] K10001 received, status=%d\n", status)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 3: Send K10002
|
// Step 3: Send K10008
|
||||||
k10002 := c.buildK10002(challenge, status)
|
k10008 := c.buildK10008(challenge, status)
|
||||||
if err := c.conn.SendIOCtrl(tutk.KCmdChallengeResp, k10002); err != nil {
|
|
||||||
return fmt.Errorf("wyze: K10002 send failed: %w", err)
|
if err := c.conn.SendIOCtrl(tutk.KCmdChallengeResp, k10008); err != nil {
|
||||||
|
return fmt.Errorf("wyze: K10008 send failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 4: Wait for K10003
|
// Step 4: Wait for K10009
|
||||||
cmdID, data, err = c.conn.RecvIOCtrl(10 * time.Second)
|
cmdID, data, err = c.conn.RecvIOCtrl(10 * time.Second)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("wyze: K10003 recv failed: %w", err)
|
return fmt.Errorf("wyze: K10009 recv failed: %w", err)
|
||||||
}
|
|
||||||
if cmdID != tutk.KCmdAuthResult {
|
|
||||||
return fmt.Errorf("wyze: expected K10003, got K%d", cmdID)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
authResp, err := c.parseK10003(data)
|
if cmdID != tutk.KCmdAuthSuccess {
|
||||||
|
return fmt.Errorf("wyze: expected K10009, got K%d", cmdID)
|
||||||
|
}
|
||||||
|
|
||||||
|
authResp, err := c.parseK10009(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("wyze: K10003 parse failed: %w", err)
|
return fmt.Errorf("wyze: K10009 parse failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse capabilities
|
// Parse capabilities
|
||||||
@@ -405,11 +407,18 @@ func (c *Client) doKAuth() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) buildK10000() []byte {
|
func (c *Client) buildK10000() []byte {
|
||||||
buf := make([]byte, 16)
|
// 137 = G.711 μ-law (PCMU)
|
||||||
|
// 138 = G.711 A-law (PCMA)
|
||||||
|
// 140 = PCM 16-bit
|
||||||
|
jsonPayload := []byte(`{"cameraInfo":{"audioEncoderList":[137,138,140]}}`)
|
||||||
|
|
||||||
|
buf := make([]byte, 16+len(jsonPayload))
|
||||||
buf[0] = 'H'
|
buf[0] = 'H'
|
||||||
buf[1] = 'L'
|
buf[1] = 'L'
|
||||||
buf[2] = 5
|
buf[2] = 5
|
||||||
binary.LittleEndian.PutUint16(buf[4:], tutk.KCmdAuth)
|
binary.LittleEndian.PutUint16(buf[4:], tutk.KCmdAuth)
|
||||||
|
binary.LittleEndian.PutUint16(buf[6:], uint16(len(jsonPayload)))
|
||||||
|
copy(buf[16:], jsonPayload)
|
||||||
return buf
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -437,6 +446,28 @@ func (c *Client) buildK10002(challenge []byte, status byte) []byte {
|
|||||||
return buf
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) buildK10008(challenge []byte, status byte) []byte {
|
||||||
|
response := crypto.GenerateChallengeResponse(challenge, c.enr, status)
|
||||||
|
openUserID := []byte(c.enr)
|
||||||
|
payloadLen := 16 + 4 + 1 + 1 + 1 + len(openUserID)
|
||||||
|
|
||||||
|
buf := make([]byte, 16+payloadLen)
|
||||||
|
buf[0] = 'H'
|
||||||
|
buf[1] = 'L'
|
||||||
|
buf[2] = 5 // Protocol version
|
||||||
|
binary.LittleEndian.PutUint16(buf[4:], tutk.KCmdAuthWithPayload) // 10008
|
||||||
|
binary.LittleEndian.PutUint16(buf[6:], uint16(payloadLen))
|
||||||
|
|
||||||
|
copy(buf[16:], response[:16]) // Challenge response
|
||||||
|
copy(buf[32:], c.uid[:4]) // UID prefix
|
||||||
|
buf[36] = 1 // Video enabled
|
||||||
|
buf[37] = 1 // Audio enabled
|
||||||
|
buf[38] = byte(len(openUserID))
|
||||||
|
copy(buf[39:], openUserID)
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client) buildK10010(mediaType byte, enabled bool) []byte {
|
func (c *Client) buildK10010(mediaType byte, enabled bool) []byte {
|
||||||
buf := make([]byte, 18)
|
buf := make([]byte, 18)
|
||||||
buf[0] = 'H'
|
buf[0] = 'H'
|
||||||
@@ -529,3 +560,42 @@ func (c *Client) parseK10003(data []byte) (*tutk.AuthResponse, error) {
|
|||||||
|
|
||||||
return &tutk.AuthResponse{}, nil
|
return &tutk.AuthResponse{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) parseK10009(data []byte) (*tutk.AuthResponse, error) {
|
||||||
|
if c.verbose {
|
||||||
|
fmt.Printf("[Wyze] parseK10009: received %d bytes\n", len(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(data) < 16 {
|
||||||
|
return &tutk.AuthResponse{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if data[0] != 'H' || data[1] != 'L' {
|
||||||
|
return &tutk.AuthResponse{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
cmdID := binary.LittleEndian.Uint16(data[4:])
|
||||||
|
textLen := binary.LittleEndian.Uint16(data[6:])
|
||||||
|
|
||||||
|
if cmdID != tutk.KCmdAuthSuccess {
|
||||||
|
return &tutk.AuthResponse{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(data) > 16 && textLen > 0 {
|
||||||
|
jsonData := data[16:]
|
||||||
|
for i := range jsonData {
|
||||||
|
if jsonData[i] == '{' {
|
||||||
|
var resp tutk.AuthResponse
|
||||||
|
if err := json.Unmarshal(jsonData[i:], &resp); err == nil {
|
||||||
|
if c.verbose {
|
||||||
|
fmt.Printf("[Wyze] parseK10009: parsed JSON\n")
|
||||||
|
}
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &tutk.AuthResponse{}, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -56,6 +56,10 @@ func NewProducer(rawURL string) (*Producer, error) {
|
|||||||
|
|
||||||
func (p *Producer) Start() error {
|
func (p *Producer) Start() error {
|
||||||
for {
|
for {
|
||||||
|
if p.client.verbose {
|
||||||
|
fmt.Println("[Wyze] Reading packet...")
|
||||||
|
}
|
||||||
|
|
||||||
_ = p.client.SetDeadline(time.Now().Add(core.ConnDeadline))
|
_ = p.client.SetDeadline(time.Now().Add(core.ConnDeadline))
|
||||||
pkt, err := p.client.ReadPacket()
|
pkt, err := p.client.ReadPacket()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -136,6 +140,10 @@ func probe(client *Client, sd bool) ([]*core.Media, error) {
|
|||||||
var tutkAudioCodec uint16
|
var tutkAudioCodec uint16
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
if client.verbose {
|
||||||
|
fmt.Println("[Wyze] Probing for codecs...")
|
||||||
|
}
|
||||||
|
|
||||||
pkt, err := client.ReadPacket()
|
pkt, err := client.ReadPacket()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("wyze: probe: %w", err)
|
return nil, fmt.Errorf("wyze: probe: %w", err)
|
||||||
|
|||||||
+27
-1
@@ -349,7 +349,33 @@ DTLS records are wrapped in IOTC DATA_TX (0x0407) packets for transmission and e
|
|||||||
|
|
||||||
```
|
```
|
||||||
Identity: "AUTHPWD_admin"
|
Identity: "AUTHPWD_admin"
|
||||||
PSK: SHA256(ENR_string) → 32 bytes
|
PSK: SHA256(ENR_string) → variable length (see below)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### PSK Length Determination
|
||||||
|
|
||||||
|
**CRITICAL**: The TUTK SDK treats the binary PSK as a NULL-terminated C string.
|
||||||
|
This means the effective PSK length is determined by the first `0x00` byte in the SHA256 hash:
|
||||||
|
|
||||||
|
```
|
||||||
|
hash = SHA256(ENR)
|
||||||
|
psk_length = position of first 0x00 byte in hash (or 32 if no 0x00)
|
||||||
|
psk = hash[0:psk_length] + zeros[psk_length:32]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Example 1** - No NULL byte in hash (full 32-byte PSK):
|
||||||
|
```
|
||||||
|
ENR: "aKzdqckqZ8HUHFe5"
|
||||||
|
SHA256: 3e5b96b8d6fc7264b531e1633de9526929d453cb47606c55d574a6e0ef5eb95f
|
||||||
|
^^ No 0x00 byte → PSK length = 32
|
||||||
|
```
|
||||||
|
|
||||||
|
**Example 2** - NULL byte at position 11 (11-byte PSK):
|
||||||
|
```
|
||||||
|
ENR: "GkB9S7cX38GgzSC6"
|
||||||
|
SHA256: 16549c533b4e9812808f91|00|95f6edf00365266f09ea1e0328df3eee1ce127ed
|
||||||
|
^^ 0x00 at position 11 → PSK length = 11
|
||||||
|
PSK: 16549c533b4e9812808f91000000000000000000000000000000000000000000
|
||||||
```
|
```
|
||||||
|
|
||||||
### Nonce Construction
|
### Nonce Construction
|
||||||
|
|||||||
+77
-94
@@ -39,10 +39,9 @@ type FrameAssembler struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Conn struct {
|
type Conn struct {
|
||||||
udpConn *net.UDPConn
|
udpConn *net.UDPConn
|
||||||
addr *net.UDPAddr
|
addr *net.UDPAddr
|
||||||
broadcastAddrs []*net.UDPAddr
|
randomID []byte
|
||||||
randomID []byte
|
|
||||||
uid string
|
uid string
|
||||||
authKey string
|
authKey string
|
||||||
enr string
|
enr string
|
||||||
@@ -100,14 +99,18 @@ func Dial(host, uid, authKey, enr, mac string, verbose bool) (*Conn, error) {
|
|||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
hash := sha256.Sum256([]byte(enr))
|
psk := derivePSK(enr)
|
||||||
psk := hash[:]
|
|
||||||
|
if verbose {
|
||||||
|
hash := sha256.Sum256([]byte(enr))
|
||||||
|
fmt.Printf("[PSK] ENR: %q → SHA256: %x\n", enr, hash)
|
||||||
|
fmt.Printf("[PSK] PSK: %x\n", psk)
|
||||||
|
}
|
||||||
|
|
||||||
c := &Conn{
|
c := &Conn{
|
||||||
udpConn: conn,
|
udpConn: conn,
|
||||||
addr: &net.UDPAddr{IP: net.ParseIP(host), Port: DefaultPort},
|
addr: &net.UDPAddr{IP: net.ParseIP(host), Port: DefaultPort},
|
||||||
broadcastAddrs: getBroadcastAddrs(DefaultPort, verbose),
|
randomID: genRandomID(),
|
||||||
randomID: genRandomID(),
|
|
||||||
uid: uid,
|
uid: uid,
|
||||||
authKey: authKey,
|
authKey: authKey,
|
||||||
enr: enr,
|
enr: enr,
|
||||||
@@ -394,8 +397,8 @@ func (c *Conn) discovery() error {
|
|||||||
newDiscoPkt := c.buildNewProtoPacket(0, 0, false) // NEW protocol (0xCC51, cmd=0x1002)
|
newDiscoPkt := c.buildNewProtoPacket(0, 0, false) // NEW protocol (0xCC51, cmd=0x1002)
|
||||||
|
|
||||||
if c.verbose {
|
if c.verbose {
|
||||||
fmt.Printf("[DISCO] Unified discovery: timeout=%v interval=%v broadcasts=%d\n",
|
fmt.Printf("[DISCO] Discovery: target=%s timeout=%v interval=%v\n",
|
||||||
DiscoTimeout, DiscoInterval, len(c.broadcastAddrs))
|
c.addr, DiscoTimeout, DiscoInterval)
|
||||||
fmt.Printf("[DISCO] SessionID=%s\n", hex.EncodeToString(c.sessionID))
|
fmt.Printf("[DISCO] SessionID=%s\n", hex.EncodeToString(c.sessionID))
|
||||||
fmt.Printf("[OLD] TX Discovery packet (%d bytes):\n%s", len(oldDiscoPkt), hexDump(crypto.ReverseTransCodeBlob(oldDiscoPkt)))
|
fmt.Printf("[OLD] TX Discovery packet (%d bytes):\n%s", len(oldDiscoPkt), hexDump(crypto.ReverseTransCodeBlob(oldDiscoPkt)))
|
||||||
fmt.Printf("[NEW] TX Discovery packet (%d bytes):\n%s", len(newDiscoPkt), hexDump(newDiscoPkt))
|
fmt.Printf("[NEW] TX Discovery packet (%d bytes):\n%s", len(newDiscoPkt), hexDump(newDiscoPkt))
|
||||||
@@ -406,12 +409,9 @@ func (c *Conn) discovery() error {
|
|||||||
buf := make([]byte, MaxPacketSize)
|
buf := make([]byte, MaxPacketSize)
|
||||||
|
|
||||||
for time.Now().Before(deadline) {
|
for time.Now().Before(deadline) {
|
||||||
// Send both discovery packets periodically
|
|
||||||
if time.Since(lastSend) >= DiscoInterval {
|
if time.Since(lastSend) >= DiscoInterval {
|
||||||
for _, bcast := range c.broadcastAddrs {
|
c.udpConn.WriteToUDP(oldDiscoPkt, c.addr)
|
||||||
c.udpConn.WriteToUDP(oldDiscoPkt, bcast) // OLD protocol
|
c.udpConn.WriteToUDP(newDiscoPkt, c.addr)
|
||||||
c.udpConn.WriteToUDP(newDiscoPkt, bcast) // NEW protocol
|
|
||||||
}
|
|
||||||
lastSend = time.Now()
|
lastSend = time.Now()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -424,6 +424,10 @@ func (c *Conn) discovery() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !addr.IP.Equal(c.addr.IP) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// Check for NEW protocol response (0xCC51 magic)
|
// Check for NEW protocol response (0xCC51 magic)
|
||||||
if n >= 12 && binary.LittleEndian.Uint16(buf[:2]) == MagicNewProto {
|
if n >= 12 && binary.LittleEndian.Uint16(buf[:2]) == MagicNewProto {
|
||||||
cmd := binary.LittleEndian.Uint16(buf[4:])
|
cmd := binary.LittleEndian.Uint16(buf[4:])
|
||||||
@@ -448,8 +452,7 @@ func (c *Conn) discovery() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if c.verbose {
|
if c.verbose {
|
||||||
fmt.Printf("[NEW] Camera detected! ticket=0x%04x sessionID=%s\n",
|
fmt.Printf("[NEW] RX Discovery Response seq=1 (%d bytes):\n%s", n, hexDump(buf[:n]))
|
||||||
ticket, hex.EncodeToString(c.sessionID))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = c.udpConn.SetDeadline(time.Time{})
|
_ = c.udpConn.SetDeadline(time.Time{})
|
||||||
@@ -571,7 +574,8 @@ func (c *Conn) newProtoComplete() error {
|
|||||||
|
|
||||||
if cmd == CmdNewProtoDiscovery && dir == 0xFFFF && seq == 3 {
|
if cmd == CmdNewProtoDiscovery && dir == 0xFFFF && seq == 3 {
|
||||||
if c.verbose {
|
if c.verbose {
|
||||||
fmt.Printf("[NEW] seq=3 received, discovery complete!\n")
|
fmt.Printf("[NEW] RX Echo Response seq=3 (%d bytes):\n%s", n, hexDump(buf[:n]))
|
||||||
|
fmt.Printf("[NEW] Discovery complete!\n")
|
||||||
}
|
}
|
||||||
c.addr = addr
|
c.addr = addr
|
||||||
return nil
|
return nil
|
||||||
@@ -634,8 +638,13 @@ func (c *Conn) iotcReader() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if addr.Port != c.addr.Port || !addr.IP.Equal(c.addr.IP) {
|
if !addr.IP.Equal(c.addr.IP) {
|
||||||
c.addr = addr
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update port if camera responds from different port
|
||||||
|
if addr.Port != c.addr.Port {
|
||||||
|
c.addr.Port = addr.Port
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for NEW protocol (0xCC51 magic at start)
|
// Check for NEW protocol (0xCC51 magic at start)
|
||||||
@@ -823,10 +832,6 @@ func (c *Conn) worker() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Conn) route(data []byte) {
|
func (c *Conn) route(data []byte) {
|
||||||
// [channel][frameType][version_lo][version_hi][seq_lo][seq_hi]...
|
|
||||||
// channel: 0x03=Audio, 0x05=I-Video, 0x07=P-Video
|
|
||||||
// frameType: 0x00=cont, 0x05=end, 0x08=I-start, 0x0d=end-44
|
|
||||||
|
|
||||||
if len(data) < 2 {
|
if len(data) < 2 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -1334,6 +1339,17 @@ func (c *Conn) buildNewProtoPacket(seq, ticket uint16, isResponse bool) []byte {
|
|||||||
authBytes := h.Sum(nil)
|
authBytes := h.Sum(nil)
|
||||||
copy(pkt[32:52], authBytes)
|
copy(pkt[32:52], authBytes)
|
||||||
|
|
||||||
|
if c.verbose {
|
||||||
|
fmt.Printf("[AUTH] Discovery Auth Debug:\n")
|
||||||
|
fmt.Printf("[AUTH] ENR: %s\n", c.enr)
|
||||||
|
fmt.Printf("[AUTH] MAC: %s\n", c.mac)
|
||||||
|
fmt.Printf("[AUTH] UID: %s\n", c.uid)
|
||||||
|
fmt.Printf("[AUTH] AuthKey: %x\n", authKey)
|
||||||
|
fmt.Printf("[AUTH] HMAC Key (UID+AuthKey): %x\n", key)
|
||||||
|
fmt.Printf("[AUTH] Hash Input (32 bytes): %x\n", pkt[:32])
|
||||||
|
fmt.Printf("[AUTH] Auth Bytes: %x\n", authBytes)
|
||||||
|
}
|
||||||
|
|
||||||
return pkt
|
return pkt
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1360,14 +1376,25 @@ func (c *Conn) buildNewProtoDTLS(payload []byte, channel byte) []byte {
|
|||||||
binary.LittleEndian.PutUint32(pkt[24:], 1) // Always 1 for DTLS wrapper
|
binary.LittleEndian.PutUint32(pkt[24:], 1) // Always 1 for DTLS wrapper
|
||||||
copy(pkt[NewProtoHeaderSize:], payload)
|
copy(pkt[NewProtoHeaderSize:], payload)
|
||||||
|
|
||||||
// Add Auth bytes at the end: HMAC-SHA1(UID+AuthKey, packet_header)
|
// Add Auth bytes at the end: HMAC-SHA1(UID+AuthKey, header only)
|
||||||
authKey := crypto.CalculateAuthKey(c.enr, c.mac)
|
authKey := crypto.CalculateAuthKey(c.enr, c.mac)
|
||||||
key := append([]byte(c.uid), authKey...)
|
key := append([]byte(c.uid), authKey...)
|
||||||
h := hmac.New(sha1.New, key)
|
h := hmac.New(sha1.New, key)
|
||||||
h.Write(pkt[:NewProtoHeaderSize]) // Hash the header portion
|
h.Write(pkt[:NewProtoHeaderSize]) // Hash the header portion only
|
||||||
authBytes := h.Sum(nil)
|
authBytes := h.Sum(nil)
|
||||||
copy(pkt[NewProtoHeaderSize+len(payload):], authBytes)
|
copy(pkt[NewProtoHeaderSize+len(payload):], authBytes)
|
||||||
|
|
||||||
|
if c.verbose {
|
||||||
|
fmt.Printf("[AUTH] DTLS Auth Debug:\n")
|
||||||
|
fmt.Printf("[AUTH] ENR: %s\n", c.enr)
|
||||||
|
fmt.Printf("[AUTH] MAC: %s\n", c.mac)
|
||||||
|
fmt.Printf("[AUTH] UID: %s\n", c.uid)
|
||||||
|
fmt.Printf("[AUTH] AuthKey: %x\n", authKey)
|
||||||
|
fmt.Printf("[AUTH] HMAC Key (UID+AuthKey): %x\n", key)
|
||||||
|
fmt.Printf("[AUTH] Hash Input (Header 28 bytes): %x\n", pkt[:NewProtoHeaderSize])
|
||||||
|
fmt.Printf("[AUTH] Auth Bytes: %x\n", authBytes)
|
||||||
|
}
|
||||||
|
|
||||||
return pkt
|
return pkt
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1742,78 +1769,34 @@ func (c *Conn) logAudioTX(frame []byte, codec uint16, payloadLen int, timestampU
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func derivePSK(enr string) []byte {
|
||||||
|
// TUTK SDK treats the PSK as a NULL-terminated C string, so if SHA256(ENR)
|
||||||
|
// contains a 0x00 byte, the PSK is truncated at that position.
|
||||||
|
// This matches iOS Wyze app behavior discovered via Frida instrumentation.
|
||||||
|
|
||||||
|
hash := sha256.Sum256([]byte(enr))
|
||||||
|
|
||||||
|
// Find first NULL byte - TUTK uses strlen() on binary PSK
|
||||||
|
pskLen := 32
|
||||||
|
for i := range 32 {
|
||||||
|
if hash[i] == 0x00 {
|
||||||
|
pskLen = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create PSK: bytes up to first 0x00, rest padded with zeros
|
||||||
|
psk := make([]byte, 32)
|
||||||
|
copy(psk[:pskLen], hash[:pskLen])
|
||||||
|
return psk
|
||||||
|
}
|
||||||
|
|
||||||
func genRandomID() []byte {
|
func genRandomID() []byte {
|
||||||
b := make([]byte, 8)
|
b := make([]byte, 8)
|
||||||
_, _ = rand.Read(b)
|
_, _ = rand.Read(b)
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
func getBroadcastAddrs(port int, verbose bool) []*net.UDPAddr {
|
|
||||||
var addrs []*net.UDPAddr
|
|
||||||
|
|
||||||
ifaces, err := net.Interfaces()
|
|
||||||
if err != nil {
|
|
||||||
if verbose {
|
|
||||||
fmt.Printf("[IOTC] Failed to get interfaces: %v\n", err)
|
|
||||||
}
|
|
||||||
// Fallback to limited broadcast
|
|
||||||
return []*net.UDPAddr{{IP: net.IPv4(255, 255, 255, 255), Port: port}}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, iface := range ifaces {
|
|
||||||
// Skip loopback and down interfaces
|
|
||||||
if iface.Flags&net.FlagLoopback != 0 || iface.Flags&net.FlagUp == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
ifAddrs, err := iface.Addrs()
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, addr := range ifAddrs {
|
|
||||||
ipNet, ok := addr.(*net.IPNet)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only IPv4
|
|
||||||
ip4 := ipNet.IP.To4()
|
|
||||||
if ip4 == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate broadcast address: IP | ~mask
|
|
||||||
mask := ipNet.Mask
|
|
||||||
if len(mask) != 4 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
broadcast := make(net.IP, 4)
|
|
||||||
for i := 0; i < 4; i++ {
|
|
||||||
broadcast[i] = ip4[i] | ^mask[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
bcastAddr := &net.UDPAddr{IP: broadcast, Port: port}
|
|
||||||
addrs = append(addrs, bcastAddr)
|
|
||||||
|
|
||||||
if verbose {
|
|
||||||
fmt.Printf("[IOTC] Found broadcast address: %s (iface: %s)\n", bcastAddr, iface.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(addrs) == 0 {
|
|
||||||
// Fallback to limited broadcast
|
|
||||||
if verbose {
|
|
||||||
fmt.Printf("[IOTC] No broadcast addresses found, using 255.255.255.255\n")
|
|
||||||
}
|
|
||||||
return []*net.UDPAddr{{IP: net.IPv4(255, 255, 255, 255), Port: port}}
|
|
||||||
}
|
|
||||||
|
|
||||||
return addrs
|
|
||||||
}
|
|
||||||
|
|
||||||
func hexDump(data []byte) string {
|
func hexDump(data []byte) string {
|
||||||
var result string
|
var result string
|
||||||
for i := 0; i < len(data); i += 16 {
|
for i := 0; i < len(data); i += 16 {
|
||||||
|
|||||||
@@ -164,6 +164,8 @@ const (
|
|||||||
KCmdChallenge = 10001
|
KCmdChallenge = 10001
|
||||||
KCmdChallengeResp = 10002
|
KCmdChallengeResp = 10002
|
||||||
KCmdAuthResult = 10003
|
KCmdAuthResult = 10003
|
||||||
|
KCmdAuthWithPayload = 10008
|
||||||
|
KCmdAuthSuccess = 10009
|
||||||
KCmdControlChannel = 10010
|
KCmdControlChannel = 10010
|
||||||
KCmdControlChannelResp = 10011
|
KCmdControlChannelResp = 10011
|
||||||
KCmdSetResolution = 10056
|
KCmdSetResolution = 10056
|
||||||
|
|||||||
Reference in New Issue
Block a user