This forum uses cookies
This forum makes use of cookies to store your login information if you are registered, and your last visit if you are not. Cookies are small text documents stored on your computer; the cookies set by this forum can only be used on this website and pose no security risk. Cookies on this forum also track the specific topics you have read and when you last read them. Please confirm that you accept these cookies being set.

IP Control of LG TVs
#1
Hi

I'm trying to implement some basic control of an LG television.  I have the protocol document from LG however haven't figured out how to script the generation of the encryption key or send the encrypted commands to the TV.

Is this possible with lua?

Turing on the device is pretty straightforward by sending a WOL packet to the MAC address of the TV, however before it will process any commands (like setting inputs, changing the volume or switching off) one must complete the handshake with the tv.

One can view the TV's pairing key code from the TV itself - it is an 8 digit key ie: BK2AT22Y

Handshake notes from LG

Client encrypts the password with PBKDF2 (Password-Based Key Derivation Function 2) method. First 16 Bytes of encrypted password are AES128 key. Following parameters must be used:
Algorithm: sha256
Salt: 0x63,0x61,0xb8,0x0e, 0x9b,0xdc,0xa6,0x63,0x8d,0x07,0x20, 0xf2,0xcc,0x56,0x8f,0xb9 Number of Iteration: 2**14

Here is the link to the full protocol  https://www.dropbox.com/s/hanc8sva8468d1...P.pdf?dl=0

Here's as far as I got with my send command

function sendCommand(command)
  socket = require("socket").tcp() 
  data, err = socket:connect('192.168.1.100', 9761)  

  -- add logic to complete handshake and encrypt command before sending

  data, err = socketConfusedend(command .. '\x0D\x0A')  
  
end


Initially all I'd like to send is the 'POWER off' and 'INPUT_SELECT hdmi1' command as detailed in the dropbox link.

Any help would be greatly appreciated.

Kind Regards
James
Reply
#2
This is possible to do in LM, can you upload PDF documentation again?
Reply
#3
Hi,

I downloaded file by previous link

Alex

Attached Files
.pdf   LG_IP.pdf (Size: 1.22 MB / Downloads: 173)
Reply
#4
Thanks for looking into this. The link should be working again now. Any help you could provide with the handshake and encryption would be greatly appreciated

Cheers
James
Reply
#5
First, you need to encrypt password using PBKDF2. Go to https://8gwifi.org/pbkdf.jsp and input the following data:
Code:
Master Password: your pairing code (BK2AT22Y)
Salt: Y2G4DpvcpmONByDyzFaPuQ==
Iteration: 16384
dkLen: 128
PBE Ciphers: PBKDF2WithHmacSHA256

Use aes.lua from this post: https://forum.logicmachine.net/showthrea...7#pid12807
Save it as a user library named aes

Example, change key to the one that you've generated. Change ip and cmd as needed. \r is added to command automatically.
Code:
ip = '192.168.1.100'
key = 'm7mRFt3BM+CwW3bYunE6sA=='
cmd = 'POWER off'

function encrypt(key, data)
  -- padding
  local rem = #data % 16
  if rem ~= 0 then
    rem = 16 - rem
    local ch = string.char(rem)
    data = data .. string.rep(ch, rem)
  end

  -- generate iv
  local iv = ''
  local ts, tu = os.microtime()
  math.randomseed(ts - tu)
  for i = 1, 16 do
    local ch = math.random(0, 255)
    iv = iv .. string.char(ch)
  end

  local aes = require('user.aes')

  -- encrypt iv
  local aes_128_ecb, err = aes:new(key, nil, aes.cipher(128, 'ecb'), { iv = string.rep('\0', 16) }, nil, 0)
  local ivenc = aes_128_ecb:encrypt(iv)

  -- encypt data
  local aes_128_cbc, err = aes:new(key, nil, aes.cipher(128, 'cbc'), { iv = iv }, nil, 0)
  local dataenc = aes_128_cbc:encrypt(data)

  return ivenc .. dataenc
end

function send(ip, key, cmd)
  local sock = require('socket').tcp()
  sock:settimeout(3)
  local res, err = sock:connect(ip, 9761)

  if res then
    key = require('encdec').base64dec(key)
    cmd = cmd .. '\r'

    local data = encrypt(key, cmd)
    res, err = sock:send(data)
    if res then
      reply = sock:receive(16)
    end
  end

  sock:close()
  return res, err
end

res, err = send(ip, key, cmd)
log(res, err)
Reply
#6
Thanks so much for this .. have implemented it this weekend and is working perfectly.
Reply
#7
Hi, can you check link again??

https://forum.logicmachine.net/showthrea...7#pid12807
Reply
#8
The link is correct now
Reply
#9
Thank you so much.
Reply
#10
(24.12.2019, 06:48)admin Wrote: First, you need to encrypt password using PBKDF2. Go to https://8gwifi.org/pbkdf.jsp and input the following data:
Code:
Master Password: your pairing code (BK2AT22Y)
Salt: Y2G4DpvcpmONByDyzFaPuQ==
Iteration: 16384
dkLen: 128
PBE Ciphers: PBKDF2WithHmacSHA256

Use aes.lua from this post: https://forum.logicmachine.net/showthrea...7#pid12807
Save it as a user library named aes

Example, change key to the one that you've generated. Change ip and cmd as needed. \r is added to command automatically.
Code:
ip = '192.168.1.100'
key = 'm7mRFt3BM+CwW3bYunE6sA=='
cmd = 'POWER off'

function encrypt(key, data)
  -- padding
  local rem = #data % 16
  if rem ~= 0 then
    rem = 16 - rem
    local ch = string.char(rem)
    data = data .. string.rep(ch, rem)
  end

  -- generate iv
  local iv = ''
  local ts, tu = os.microtime()
  math.randomseed(ts - tu)
  for i = 1, 16 do
    local ch = math.random(0, 255)
    iv = iv .. string.char(ch)
  end

  local aes = require('user.aes')

  -- encrypt iv
  local aes_128_ecb, err = aes:new(key, nil, aes.cipher(128, 'ecb'), { iv = string.rep('\0', 16) }, nil, 0)
  local ivenc = aes_128_ecb:encrypt(iv)

  -- encypt data
  local aes_128_cbc, err = aes:new(key, nil, aes.cipher(128, 'cbc'), { iv = iv }, nil, 0)
  local dataenc = aes_128_cbc:encrypt(data)

  return ivenc .. dataenc
end

function send(ip, key, cmd)
  local sock = require('socket').tcp()
  sock:settimeout(3)
  local res, err = sock:connect(ip, 9761)

  if res then
    key = require('encdec').base64dec(key)
    cmd = cmd .. '\r'

    local data = encrypt(key, cmd)
    res, err = sock:send(data)
    if res then
      reply = sock:receive(16)
    end
  end

  sock:close()
  return res, err
end

res, err = send(ip, key, cmd)
log(res, err)
Hi
i have the above all working, Many Thanks, how can i decrypt the response from the command sent, i added log (reply) and this is the result 
string %9����i���%r4

do i need to use 
Code:
local aes_128_cbcc, err = aes:new(key, nil, aes.cipher(128, 'cbc'), { iv = iv }, nil, 0)
local datadec = aes_128_cbcc:decrypt(reply)


where do i get the iv from, the doc say to use received iv as below?

5)
Receiver applies decryption with received IV, and encrypted message. For “VOLUME_MUTE on” command, the response from the server is: ce 53 7c e4 b8 82 98 c6 a4 3e 21 89 af 5f 20 2f and after decryption: 4f 4b 0a 00 7d 7d 7d 7d 7d 7d 7d 7d 7d 7d 7d 7d. O K ‘\n’ } } } } } } } } } } } } For now, just ignore characters after ‘\n’
Reply
#11
You can use loghex(data) to display binary data as hex. The receiving part of this example is incomplete. First 16 bytes of the reply contain the encrypted IV then one or more 16 byte blocks should follow. Receiving should stop if a decrypted block contains \n character.
Reply
#12
(24.12.2020, 08:58)admin Wrote: You can use loghex(data) to display binary data as hex. The receiving part of this example is incomplete. First 16 bytes of the reply contain the encrypted IV then one or more 16 byte blocks should follow. Receiving should stop if a decrypted block contains \n character.
ok i understand now, so if i read 32 bytes then split into 2 local var then use the iv in the first 16 bytes with the aes ecb, i will get the decrypted response then i need to split the string at \n to receive the full response

i have it with the below

Code:
reply = sock:receive(32)
     
      res_iv = string.sub(reply, 0, 16)
      enc_res = string.sub(reply, 17, 32)
   
      -- decript iv
      local aes_128_ecb, err = aes:new(key, nil, aes.cipher(128, 'ecb'), { iv = string.rep('\0', 16) }, nil, 0)
      local ivdec = aes_128_ecb:decrypt(res_iv)
     
      -- decrypt encryped reply
      local aes_128_cbcc, err = aes:new(key, nil, aes.cipher(128, 'cbc'), { iv = ivdec}, nil, 0)
      local datadec = aes_128_cbcc:decrypt(enc_res)
      loghex(datadec)
Reply
#13
Try this function:
Code:
function receive(sock)
  local iv = sock:receive(16)
  if not iv then
    return nil, 'cannot receive iv'
  end

  local aes_128_ecb, err = aes:new(key, nil, aes.cipher(128, 'ecb'), { iv = string.rep('\0', 16) }, nil, 0)

  local ivdec = aes_128_ecb:decrypt(iv)
  local aes_128_cbc, err = aes:new(key, nil, aes.cipher(128, 'cbc'), { iv = ivdec }, nil, 0)

  local buf = ''
  sock:settimeout(0.1)

  while true do
    local data, err = sock:receive(16)
    if data then
      buf = buf .. data
    else
      break
    end
  end

  sock:settimeout(3)

  if #buf > 0 then
    return aes_128_cbc:decrypt(buf)
  else
    return nil, 'receive failed'
  end
end

Use this like this:
Code:
reply, err = receive(sock)
log(reply, err)
Reply
#14
Many Thanks,

my first attempt only received half the string, now with your function i receive the complete response.
Smile
Reply
#15
hello wanted to ask if this can be done using windows 10 and maybe a powershell script? i wanted to make a powershell to change to hdmi 1 or hdmi 2
Reply
#16
Not the right place to ask but on Windows you can run Python, have a look at this library: https://github.com/supersaiyanmode/PyWebOSTV
Reply
#17
When I run this program, I receive the following log: * arg: 1
  * number: 32
* arg: 2
  * nil
and i can't control my tv .
can you help me!

(04.05.2020, 05:19)admin Wrote: The link is correct now

When I run this program, I receive the following log: * arg: 1
  * number: 32
* arg: 2
  * nil
and i can't control my tv .
can you help me!
Reply
#18
This is not guaranteed to work on all TV models, there might be some changes to the protocol depending on the model year. Check that your script has correct key variable set. Follow this guide: https://forum.logicmachine.net/showthrea...1#pid15091 and make sure that you input correct pairing code from your TV and use correct hashing parameters (Salt, Iteration, dkLen, PBE Cipher).
Reply


Forum Jump: