Posts: 86
Threads: 10
Joined: Jul 2015
Reputation:
1
Dear friends,
According
to this site , it seems that there a possibility to control Panasonic Viera TVs through IP commands.
Since my LUA knowledge is very starter level, can anyone give me a quick example how to send a simple "power off" command through LUA scripting so I can try to move it forward with the rest of the commands?
Thank you all in advance.
Posts: 8071
Threads: 43
Joined: Jun 2015
Reputation:
471
Try these functions, they are untested so might not work at all. You have to parse any XML response manually
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
function request (
host ,
url ,
urn ,
action ,
args )
local body ,
http ,
ltn12 ,
sink ,
res ,
err
ltn12 =
require (
'ltn12' )
http =
require (
'socket.http' )
body =
[[<?xml version="1.0" encoding="utf-8"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<s:Body><u:]] ..
action ..
[[ xmlns:u="urn:]] ..
urn ..
[[">]] ..
args ..
[[</u:]] ..
action ..
[[></s:Body>
</s:Envelope>]]
sink = {}
res ,
err =
http.request ({
url =
'http://' ..
host ..
':55000/' ..
url ,
method =
'POST' ,
headers = {
[
'soapaction' ] =
'"urn:' ..
urn ..
'#' ..
action ..
'"' ,
[
'Content-Length' ] = #
body ,
},
sink =
ltn12.sink.table (
sink ),
source =
ltn12.source.string (
body ),
})
if res then
return table.concat (
sink )
else
return nil ,
err
end
end
function sendkey (
host ,
code )
local args =
'<X_KeyEvent>' ..
code ..
'</X_KeyEvent>'
return request (
host ,
'nrc/control_0' ,
'panasonic-com:service:p00NetworkControl:1' ,
'X_SendKey' ,
args )
end
function getmute (
host )
return request (
host ,
'dmr/control_0' ,
'schemas-upnp-org:service:RenderingControl:1' ,
'GetMute' ,
'<InstanceID>0</InstanceID><Channel>Master</Channel>' )
end
IP =
'192.168.1.2'
log (
getmute (
IP ))
Posts: 86
Threads: 10
Joined: Jul 2015
Reputation:
1
15.03.2016, 14:55
(This post was last modified: 15.03.2016, 15:16 by jetsetter .)
Thank you very much.
I will give it a try and report back.
I have successfully add a user library with those functions and then have send a "NRC_POWER-ONOFF" code to the TV and it worked!! Thank you very much!
I will now try to complete the rest of the commands and - if possible - to create a simple panel to simulate the remote control via the LM visualization!
One last question, since this TV is not powered via a bus-controlable outlet, and also does not support any wake-on-lan functionality, in order to update the TV's "ON" status after I turn it off via the above IP command, is there a way to periodicaly check if a specific IP is alive (via ping?) or a specific domain name is alive (it always register to DHCP as "COM-MID1") in my LAN?
Than you in advance.
Posts: 8071
Threads: 43
Joined: Jun 2015
Reputation:
471
You can use this function to check if given TCP port on your device is accepting connections:
Code:
1 2 3 4 5 6 7 8 9 10 11 12
function socketping (
ip ,
port ,
timeout )
local sock =
require (
'socket' ).
tcp ()
sock :
settimeout (
timeout or 2 )
local res ,
err =
sock :
connect (
ip ,
port )
sock :
close ()
return res ,
err
end
IP =
'192.168.1.10'
PORT =
55000
status =
socketping (
IP ,
PORT )
Posts: 86
Threads: 10
Joined: Jul 2015
Reputation:
1
16.03.2016, 15:05
Just add this to the rest of TV functions and works great too!
Thank you once more for your support.
My next try will be to control another TV that doesn't have native IP control (no LAN port at all). This will be done through HDMI CEC commands that will be send via Kodi (formerly XBMC media player) running on a RPI, through Kodi's JSON RPC API and a relative Kodi add-on, so I will update with the results and more details upon success...
Thanks!
Posts: 86
Threads: 10
Joined: Jul 2015
Reputation:
1
Just finished successfully also the power-off control of another non-smart TV, through a Kodi media player (running in an RPi) connected to it via HDMI.
If anyone is interested, the code I used is the following:
Code:
1 2 3 4 5
require (
'socket.http' )
socket.http.TIMEOUT =
5
data1 =
socket.http.request (
'http://KODI-IP-ADDRESS/jsonrpc?request={"jsonrpc":"2.0","method":"Addons.ExecuteAddon","params":{"addonid":"script.json-cec","params":{"command":"toggle"}},"id":1}' )
socket.http.TIMEOUT =
5
data2 =
socket.http.request (
'http://KODI-IP-ADDRESS/jsonrpc?request={"jsonrpc":"2.0","method":"Addons.ExecuteAddon","params":{"addonid":"script.json-cec","params":{"command":"standby"}},"id":1}' )
Sends two CEC commands to the TV through HDMI. One is for swapping the TV source to the HDMI of KODI, and the second is for putting the TV to standby (the TV should be on this source in order for the standby command to work).
The KODI add-on used for the above script can be found
here:
Posts: 85
Threads: 16
Joined: Jun 2016
Reputation:
2
Hello,
I have a Samsung smart tv and i want to control TV through IP commands.
Have you a script or a app for that?
Thanks.
Posts: 92
Threads: 9
Joined: Sep 2015
Reputation:
1
I need for Sony Bravia Android TVs, too!
Posts: 133
Threads: 28
Joined: May 2016
Reputation:
3
Hi,
I wanted to ask if there was already progress?
I would like to control a SamsungTV with a homeLYnk. Is it with the CEC / HDMI adapter? and does the adapter work with space- / homeLYnk's as well?
Posts: 8071
Threads: 43
Joined: Jun 2015
Reputation:
471
Most TVs have limited CEC support (not all functions are supported). Another issue is that some models cannot be turned on with CEC, only turned off. So you need to do so digging before buying an adapter
Posts: 133
Threads: 28
Joined: May 2016
Reputation:
3
thx. I've ordered one Chromecast to test it out like describe's in example section.
Posts: 139
Threads: 44
Joined: Dec 2017
Reputation:
4
(10.03.2016, 14:32) admin Wrote: Try these functions, they are untested so might not work at all. You have to parse any XML response manually
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
function request (
host ,
url ,
urn ,
action ,
args )
local body ,
http ,
ltn12 ,
sink ,
res ,
err
ltn12 =
require (
'ltn12' )
http =
require (
'socket.http' )
body =
[[<?xml version="1.0" encoding="utf-8"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<s:Body><u:]] ..
action ..
[[ xmlns:u="urn:]] ..
urn ..
[[">]] ..
args ..
[[</u:]] ..
action ..
[[></s:Body>
</s:Envelope>]]
sink = {}
res ,
err =
http.request ({
url =
'http://' ..
host ..
':55000/' ..
url ,
method =
'POST' ,
headers = {
[
'soapaction' ] =
'"urn:' ..
urn ..
'#' ..
action ..
'"' ,
[
'Content-Length' ] = #
body ,
},
sink =
ltn12.sink.table (
sink ),
source =
ltn12.source.string (
body ),
})
if res then
return table.concat (
sink )
else
return nil ,
err
end
end
function sendkey (
host ,
code )
local args =
'<X_KeyEvent>' ..
code ..
'</X_KeyEvent>'
return request (
host ,
'nrc/control_0' ,
'panasonic-com:service:p00NetworkControl:1' ,
'X_SendKey' ,
args )
end
function getmute (
host )
return request (
host ,
'dmr/control_0' ,
'schemas-upnp-org:service:RenderingControl:1' ,
'GetMute' ,
'<InstanceID>0</InstanceID><Channel>Master</Channel>' )
end
IP =
'192.168.1.2'
log (
getmute (
IP ))
Now in my region as of 2019 Panasonic have implemented Auth before i can send commands,
i found this (
https://github.com/florianholzapfel/pana...a/issues/9 ) explaining how it is achieved, but it is python, im not sure where to start to work out how to port it to lua,
I have soap command working to return the iv key my goal is to return the encrypted_payload to send to the tv to make connection.
import binascii
import base64
import hmac, hashlib
from Crypto.Cipher import AES
# Example challenge (which is our IV)
iv = base64.b64decode("mUQdS7/RyJTMsiojPz9i1Q==" )
# Get character codes from IV bytes
iv_vals = [ord © for c in iv]
# Initialise key character codes array
key_vals = [0 ] * 16
# Derive key from IV
i = 0
while i < 16 :
key_vals[i] = ~ iv_vals[i + 3 ] & 0x FF
key_vals[i + 1 ] = ~ iv_vals[i + 2 ] & 0x FF
key_vals[i + 2 ] = ~ iv_vals[i + 1 ] & 0x FF
key_vals[i + 3 ] = ~ iv_vals[i] & 0x FF
i += 4
# Convert our key character codes to bytes
key = '' .join(chr © for c in key_vals)
# Initialise HMAC key mask (taken from libtvconnect.so)
hmac_key_mask_vals = [ord © for c in binascii.unhexlify("15C95AC2B08AA7EB4E228F811E34D04FA54BA7DCAC9879FA8ACDA3FC244F3854" )]
# Initialise HMAC key character codes array
hmac_vals = [0 ] * 32
# Calculate HMAC key using HMAC key mask and IV
i = 0
while i < 32 :
hmac_vals[i] = hmac_key_mask_vals[i] ^ iv_vals[(i + 2 ) & 0x F ]
hmac_vals[i + 1 ] = hmac_key_mask_vals[i + 1 ] ^ iv_vals[(i + 3 ) & 0x F ]
hmac_vals[i + 2 ] = hmac_key_mask_vals[i + 2 ] ^ iv_vals[i & 0x F ]
hmac_vals[i + 3 ] = hmac_key_mask_vals[i + 3 ] ^ iv_vals[(i + 1 ) & 0x F ]
i += 4
# Convert our HMAC key character codes to bytes
hmac_key = '' .join(chr © for c in hmac_vals)
# This is our plaintext SOAP argument for the pin code shown on the TV
authinfo = "<X_PinCode>4410</X_PinCode>"
# First 12 bytes are randomised, let's just set them to 0 because it doesn't matter
payload = "000000000000"
# The next 4 bytes contain the plaintext (SOAP arg) length in big endian
n = len (authinfo)
payload += chr (n >> 24 )
payload += chr ((n >> 16 ) & 0x FF )
payload += chr ((n >> 8 ) & 0x FF )
payload += chr (n & 0x FF )
# Now we concatenate our payload, which is starting at byte 17 of the payload
payload += authinfo
# Let's encrypt it with AES-CBC! We need to make sure we pad it to a multiple of 16 bytes beforehand
aes = AES .new(key, AES .MODE_CBC , iv)
ciphertext = aes.encrypt(pad(payload))
# Calculate the HMAC-SHA-256 signature of our encrypted payload
sig = hmac.new(hmac_key, ciphertext, hashlib.sha256).digest()
# Concatenate the HMAC signature to the encrypted payload and base64 encode it, and we're done!
encrypted_payload = base64.b64encode(ciphertext + sig)
Posts: 8071
Threads: 43
Joined: Jun 2015
Reputation:
471
For AES functions use library in this post:
https://forum.logicmachine.net/showthread.php?tid=2057
encdec libary supports following HMAC functions:
hmacmd5
hmacsha1
hmacsha256
hmacsha384
hmacsha512
function arguments:
data
key
raw (set to true to get raw binary hmac value, hex otherwise)
Posts: 139
Threads: 44
Joined: Dec 2017
Reputation:
4
(20.01.2020, 08:32) admin Wrote: For AES functions use library in this post: https://forum.logicmachine.net/showthread.php?tid=2057
for Aes functions, what format can the key and hash need to be in ? string, table, byte array? etc,
i have the below it runs with no err but result not correct i think key an iv need to be in a different format?
key = 'u�НJ�z�6���Y+�'
iv = 'b/�~��ss��'
payload = 'some data'
local aes, err = aes:new(key, nil, aes.cipher(256, 'cbc'), iv, nil, 0)
ciphertext = aes.encrypt(payload)
log(ciphertext)
Posts: 8071
Threads: 43
Joined: Jun 2015
Reputation:
471
Here's Python code converted to Lua:
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
require (
'encdec' )
aes =
require (
'user.aes' )
iv =
encdec.base64dec (
'mUQdS7/RyJTMsiojPz9i1Q==' )
iv_vals = {
iv :
byte (
1 , -
1 ) }
key_vals = {}
for i =
1 ,
16 ,
4 do
key_vals [
i ] =
bit.band (
bit.bnot (
iv_vals [
i +
3 ]),
0xFF )
key_vals [
i +
1 ] =
bit.band (
bit.bnot (
iv_vals [
i +
2 ]),
0xFF )
key_vals [
i +
2 ] =
bit.band (
bit.bnot (
iv_vals [
i +
1 ]),
0xFF )
key_vals [
i +
3 ] =
bit.band (
bit.bnot (
iv_vals [
i ]),
0xFF )
end
key =
string.char (
unpack (
key_vals ))
hmac_key_mask =
lmcore.hextostr (
'15C95AC2B08AA7EB4E228F811E34D04FA54BA7DCAC9879FA8ACDA3FC244F3854' ,
true )
hmac_key_mask_vals = {
hmac_key_mask :
byte (
1 , -
1 ) }
hmac_vals = {}
for i =
1 ,
32 ,
4 do
hmac_vals [
i ] =
bit.bxor (
hmac_key_mask_vals [
i ],
iv_vals [
bit.band (
i +
1 ,
0xF ) +
1 ])
hmac_vals [
i +
1 ] =
bit.bxor (
hmac_key_mask_vals [
i +
1 ],
iv_vals [
bit.band (
i +
2 ,
0xF ) +
1 ])
hmac_vals [
i +
2 ] =
bit.bxor (
hmac_key_mask_vals [
i +
2 ],
iv_vals [
bit.band (
i -
1 ,
0xF ) +
1 ])
hmac_vals [
i +
3 ] =
bit.bxor (
hmac_key_mask_vals [
i +
3 ],
iv_vals [
bit.band (
i ,
0xF ) +
1 ])
end
hmac_key =
string.char (
unpack (
hmac_vals ))
authinfo =
'<X_PinCode>4410</X_PinCode>'
payload =
'000000000000'
n = #
authinfo
payload =
payload ..
string.char (
bit.band (
bit.rshift (
n ,
24 ),
0xFF ))
payload =
payload ..
string.char (
bit.band (
bit.rshift (
n ,
16 ),
0xFF ))
payload =
payload ..
string.char (
bit.band (
bit.rshift (
n ,
8 ),
0xFF ))
payload =
payload ..
string.char (
bit.band (
n ,
0xFF ))
payload =
payload ..
authinfo
aes_cbc ,
err =
aes :
new (
key ,
nil ,
aes.cipher (
128 ,
'cbc' ), {
iv =
iv },
nil ,
1 )
ciphertext =
aes_cbc :
encrypt (
payload )
sig =
encdec.hmacsha256 (
ciphertext ,
hmac_key ,
true )
encrypted_payload =
encdec.base64enc (
ciphertext ..
sig )
Posts: 139
Threads: 44
Joined: Dec 2017
Reputation:
4
(14.04.2020, 07:57) admin Wrote: Here's Python code converted to Lua: Thanks @admin
I have it working now
im not sure what is the best way to pass variables between functions is, i used storage... is there any way to make variables global and persistant in a user library also is there any improvements i can make to this script, still learning...
To get a pin to display on the tv call function request_pin_code()
once you have the pin displayed on the tv then call function authorise_pin_code('0080') with the pin from the tv, that completes the pairing process, the tv will show message "pairing process complete"
Then you can call send encrypted commands with function, send_key('NRC_POWER-ONOFF') etc
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
encdec =
require (
'encdec' )
aes =
require (
'user.aes' )
require (
'json' )
ip =
'192.168.1.100'
name =
'My Remote'
URL_CONTROL_NRC =
'nrc/control_0'
URL_CONTROL_DMR =
'dmr/control_0'
URL_CONTROL_NRC_DEF =
'nrc/sdd_0.xml'
URN_RENDERING_CONTROL =
'schemas-upnp-org:service:RenderingControl:1'
URN_REMOTE_CONTROL =
'panasonic-com:service:p00NetworkControl:1'
function request (
host ,
url ,
urn ,
action ,
args )
local body ,
http ,
ltn12 ,
sink ,
res ,
err
ltn12 =
require (
'ltn12' )
http =
require (
'socket.http' )
body =
[[<?xml version="1.0" encoding="utf-8"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<s:Body><u:]] ..
action ..
[[ xmlns:u="urn:]] ..
urn ..
[[">]] ..
args ..
[[</u:]] ..
action ..
[[></s:Body>
</s:Envelope>]]
sink = {}
res ,
err =
http.request ({
url =
'http://' ..
host ..
':55000/' ..
url ,
method =
'POST' ,
headers = {
[
'soapaction' ] =
'"urn:' ..
urn ..
'#' ..
action ..
'"' ,
[
'Content-Type' ] =
'text/xml; charset=utf-8' ,
[
'Content-Length' ] = #
body ,
},
sink =
ltn12.sink.table (
sink ),
source =
ltn12.source.string (
body ),
})
if sink then
return table.concat (
sink )
else
return nil ,
err
end
end
function encrypt_soap_payload (
data ,
key ,
hmac_key ,
iv )
payload =
'000000000000'
n = #
data
payload =
payload ..
string.char (
bit.band (
bit.rshift (
n ,
24 ),
0xFF ))
payload =
payload ..
string.char (
bit.band (
bit.rshift (
n ,
16 ),
0xFF ))
payload =
payload ..
string.char (
bit.band (
bit.rshift (
n ,
8 ),
0xFF ))
payload =
payload ..
string.char (
bit.band (
n ,
0xFF ))
payload =
payload ..
data
aes_cbc ,
err =
aes :
new (
key ,
nil ,
aes.cipher (
128 ,
'cbc' ), {
iv =
iv },
nil ,
1 )
ciphertext =
aes_cbc :
encrypt (
payload )
sig =
encdec.hmacsha256 (
ciphertext ,
hmac_key ,
true )
encrypted_payload =
encdec.base64enc (
ciphertext ..
sig )
return encrypted_payload
end
function decrypt_soap_payload (
data ,
key ,
hmac_key ,
iv )
aes_cbc ,
err =
aes :
new (
key ,
nil ,
aes.cipher (
128 ,
'cbc' ), {
iv =
iv },
nil ,
0 )
decrypted =
aes_cbc :
decrypt (
encdec.base64dec (
data ))
decrypted =
string.gsub (
string.sub (
lmcore.strtohex (
decrypted ),
33 ),
'%x%x' ,
function (
value )
return string.char (
tonumber (
value ,
16 ))
end )
return decrypted
end
function get_session_keys (
enc_key )
iv =
encdec.base64dec (
enc_key )
iv_vals = {
iv :
byte (
1 , -
1 ) }
key_vals = {}
for i =
1 ,
16 ,
4 do
key_vals [
i ] =
iv_vals [
i +
2 ]
key_vals [
i +
1 ] =
iv_vals [
i +
3 ]
key_vals [
i +
2 ] =
iv_vals [
i ]
key_vals [
i +
3 ] =
iv_vals [
i +
1 ]
end
sesh_key =
string.char (
unpack (
key_vals ))
sesh_hmac_key =
iv..iv
sesh_iv =
iv
return sesh_key ,
sesh_hmac_key ,
sesh_iv
end
function request_session_id (
app_id ,
sesh_key ,
sesh_hmac_key ,
sesh_iv )
encinfo =
encrypt_soap_payload (
'<X_ApplicationId>' ..
app_id ..
'</X_ApplicationId>' ,
sesh_key ,
sesh_hmac_key ,
sesh_iv )
params = (
'<X_ApplicationId>' ..
app_id.. '</X_ApplicationId>' ..
'<X_EncInfo>' ..
encinfo.. '</X_EncInfo>' )
sesh_id =
request (
ip ,
URL_CONTROL_NRC ,
URN_REMOTE_CONTROL ,
'X_GetEncryptSessionId' ,
params )
return sesh_id
end
function request_pin_code ()
pairing_res =
request (
ip ,
URL_CONTROL_NRC ,
URN_REMOTE_CONTROL ,
'X_DisplayPinCode' ,
'<X_DeviceName>' ..
name ..
'</X_DeviceName>' )
if pairing_res then
challenge_Key =
string.match (
pairing_res ,
'<X_ChallengeKey>(.*)</X_ChallengeKey>' )
iv =
encdec.base64dec (
challenge_Key )
storage.set (
'IV' ,
iv )
end
end
function authorise_pin_code (
pincode )
iv =
storage.get (
'IV' )
iv_vals = {
iv :
byte (
1 , -
1 ) }
key_vals = {}
for i =
1 ,
16 ,
4 do
key_vals [
i ] =
bit.band (
bit.bnot (
iv_vals [
i +
3 ]),
0xFF )
key_vals [
i +
1 ] =
bit.band (
bit.bnot (
iv_vals [
i +
2 ]),
0xFF )
key_vals [
i +
2 ] =
bit.band (
bit.bnot (
iv_vals [
i +
1 ]),
0xFF )
key_vals [
i +
3 ] =
bit.band (
bit.bnot (
iv_vals [
i ]),
0xFF )
end
key =
string.char (
unpack (
key_vals ))
hmac_key_mask =
lmcore.hextostr (
'15C95AC2B08AA7EB4E228F811E34D04FA54BA7DCAC9879FA8ACDA3FC244F3854' ,
true )
hmac_key_mask_vals = {
hmac_key_mask :
byte (
1 , -
1 ) }
hmac_vals = {}
for i =
1 ,
32 ,
4 do
hmac_vals [
i ] =
bit.bxor (
hmac_key_mask_vals [
i ],
iv_vals [
bit.band (
i +
1 ,
0xF ) +
1 ])
hmac_vals [
i +
1 ] =
bit.bxor (
hmac_key_mask_vals [
i +
1 ],
iv_vals [
bit.band (
i +
2 ,
0xF ) +
1 ])
hmac_vals [
i +
2 ] =
bit.bxor (
hmac_key_mask_vals [
i +
2 ],
iv_vals [
bit.band (
i -
1 ,
0xF ) +
1 ])
hmac_vals [
i +
3 ] =
bit.bxor (
hmac_key_mask_vals [
i +
3 ],
iv_vals [
bit.band (
i ,
0xF ) +
1 ])
end
hmac_key =
string.char (
unpack (
hmac_vals ))
params =
'<X_AuthInfo>' ..
encrypt_soap_payload (
"<X_PinCode>" ..
pincode ..
"</X_PinCode>" ,
key ,
hmac_key ,
iv ) ..
'</X_AuthInfo>'
authorise_res =
request (
ip ,
URL_CONTROL_NRC ,
URN_REMOTE_CONTROL ,
'X_RequestAuth' ,
params )
auth_res =
string.match (
authorise_res ,
'<X_AuthResult>(.*)</X_AuthResult>' )
decrypted =
decrypt_soap_payload (
auth_res ,
key ,
hmac_key ,
iv )
app_id =
string.match (
decrypted ,
'<X_ApplicationId>(.*)</X_ApplicationId>' )
enc_key =
string.match (
decrypted ,
'<X_Keyword>(.*)</X_Keyword>' )
sesh_key ,
sesh_hmac_key ,
sesh_iv =
get_session_keys (
enc_key )
sesh_id_res =
request_session_id (
app_id ,
sesh_key ,
sesh_hmac_key ,
sesh_iv )
enc_result =
string.match (
sesh_id_res ,
'<X_EncResult>(.*)</X_EncResult>' )
enc_result =
decrypt_soap_payload (
enc_result ,
sesh_key ,
sesh_hmac_key ,
sesh_iv )
sesh_id =
string.match (
enc_result ,
'<X_SessionId>(.*)</X_SessionId>' )
sesh_seq_num =
1
session_keys =
json.encode ({
SESSION_ID =
sesh_id ,
SESSION_SEQUENCE_NUMBER =
sesh_seq_num ,
SESSION_IV =
sesh_iv ,
SESSION_HMAC_KEY =
sesh_hmac_key ,
SESSION_KEY =
sesh_key ,
APP_ID =
app_id ,
KEY =
key ,
HMAC_KEY =
hmac_key ,
IV =
iv
})
storage.set (
'session_keys' ,
session_keys )
return sesh_id ,
sesh_seq_num ,
sesh_iv
end
function send_enc_cmd (
action ,
urn ,
params )
x =
storage.get (
'session_keys' )
S_K =
json.decode (
x )
seq_num =
string.format (
"%08d" ,
S_K.SESSION_SEQUENCE_NUMBER )
encrypted_command =
[[<X_SessionId>]] ..
S_K.SESSION_ID ..
[[</X_SessionId>]] ..
[[<X_SequenceNumber>]] ..
seq_num ..
[[</X_SequenceNumber>]] ..
[[<X_OriginalCommand>]] ..
[[<u:]] ..
action ..
[[ xmlns:u="urn:]] ..
urn ..
[[">]] ..
params ..
[[</u:]] ..
action ..
[[>]] ..
[[</X_OriginalCommand>]]
enc_cmd =
encrypt_soap_payload (
encrypted_command ,
S_K.SESSION_KEY ,
S_K.SESSION_HMAC_KEY ,
S_K.SESSION_IV )
params =
'<X_ApplicationId>' ..
S_K.APP_ID.. '</X_ApplicationId>' ..
'<X_EncInfo>' ..
enc_cmd.. '</X_EncInfo>'
send_enc_cmd_res =
request (
ip ,
URL_CONTROL_NRC ,
URN_REMOTE_CONTROL ,
'X_EncryptedCommand' ,
params )
res_err =
string.match (
send_enc_cmd_res ,
'<s:Fault>' )
if not res_err then
S_K.SESSION_SEQUENCE_NUMBER =
S_K.SESSION_SEQUENCE_NUMBER +
1
session_keys =
json.encode ({
SESSION_ID =
S_K.SESSION_ID ,
SESSION_SEQUENCE_NUMBER =
S_K.SESSION_SEQUENCE_NUMBER ,
SESSION_IV =
S_K.SESSION_IV ,
SESSION_HMAC_KEY =
S_K.SESSION_HMAC_KEY ,
SESSION_KEY =
S_K.SESSION_KEY ,
APP_ID =
S_K.APP_ID ,
KEY =
S_K.KEY ,
HMAC_KEY =
S_K.HMAC_KEY ,
IV =
S_K.IV
})
storage.set (
'session_keys' ,
session_keys )
end
return params
end
function send_key (
key )
params =
send_enc_cmd (
'X_SendKey' ,
URN_REMOTE_CONTROL ,
'<X_KeyEvent>' ..
key.. '</X_KeyEvent>' )
end
function get_mute ()
req_res =
request (
ip ,
URL_CONTROL_DMR ,
URN_RENDERING_CONTROL ,
'GetMute' ,
'<InstanceID>0</InstanceID><Channel>Master</Channel>' )
mute_level =
string.match (
req_res ,
'<CurrentMute>(.*)</CurrentMute>' )
return mute_level
end
function get_volume ()
req_res =
request (
ip ,
URL_CONTROL_DMR ,
URN_RENDERING_CONTROL ,
'GetVolume' ,
'<InstanceID>0</InstanceID><Channel>Master</Channel>' )
vol_level =
string.match (
req_res ,
'<CurrentVolume>(.*)</CurrentVolume>' )
return vol_level
end
function set_mute (
state )
request (
ip ,
URL_CONTROL_DMR ,
URN_RENDERING_CONTROL ,
'SetMute' ,
'<InstanceID>0</InstanceID><Channel>Master</Channel><DesiredMute>' ..
state ..
'</DesiredMute>' )
end
function set_volume (
level )
request (
ip ,
URL_CONTROL_DMR ,
URN_RENDERING_CONTROL ,
'SetVolume' ,
'<InstanceID>0</InstanceID><Channel>Master</Channel><DesiredVolume>' ..
level ..
'</DesiredVolume>' )
end
Posts: 2
Threads: 0
Joined: Mar 2020
Reputation:
0
24.12.2020, 18:46
(This post was last modified: 27.12.2020, 13:39 by parkerc .)
Hi
id love to test this piece of code on a standard Lua installation, but to do that does anyone know where I could find the required modules/libraries ?
Code:
1 2
encdec =
require (
'encdec' )
aes =
require (
'user.aes' )
Is anyone able to post them or direct me to where I can download them ?
Posts: 8071
Threads: 43
Joined: Jun 2015
Reputation:
471
This code requires LuaJIT. It won't work with standard Lua because it does not have FFI library.
Posts: 2
Threads: 0
Joined: Mar 2020
Reputation:
0
27.12.2020, 14:04
(This post was last modified: 27.12.2020, 14:23 by parkerc .)
(25.12.2020, 13:49) admin Wrote: This code requires LuaJIT. It won't work with standard Lua because it does not have FFI library.
Can I assume an alternative Lua library (or libraries) could be used ?
For example to get around the
encdec.base64dec() requirement, I used a base64 function/code from here -
http://lua-users.org/wiki/BaseSixtyFour and added a specific new function called
encdec_base64dec()
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
function encdec_base64dec (
data )
local b =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
data =
string.gsub (
data ,
'[^' ..
b.. '=]' ,
'' )
return (
data :
gsub (
'.' ,
function (
x )
if (
x ==
'=' )
then return '' end
local r ,
f =
'' ,(
b :
find (
x )-
1 )
for i =
6 ,
1 ,-
1 do r =
r.. (
f %
2 ^
i-f %
2 ^(
i-1 )>
0 and '1' or '0' )
end
return r ;
end ):
gsub (
'%d%d%d?%d?%d?%d?%d?%d?' ,
function (
x )
if (#
x ~=
8 )
then return '' end
local c =
0
for i =
1 ,
8 do c =
c +(
x :
sub (
i ,
i )==
'1' and 2 ^(
8 -
i )
or 0 )
end
return string.char (
c )
end ))
end
I would really appreciate any guidance on alternatives for other calls such as those below e.g hex to/from string and aes_cbc encrypt/decrypt etc.
Code:
1 2 3 4 5
lmcore.strtohex (
decrypted )
lmcore.hextostr (
'15C95AC2B08AA7EB4E228F811E34D04FA54BA7DCAC9879FA8ACDA3FC244F3854' ,
true )
aes_cbc :
decrypt (
encdec.base64dec (
data ))
aes_cbc :
encrypt (
payload )
encdec.hmacsha256 (
ciphertext ,
hmac_key ,
true )
Is something like
https://github.com/somesocks/lua-lockbox or something similar potentially a viable alternative library?
Posts: 8071
Threads: 43
Joined: Jun 2015
Reputation:
471
For conversion from/to hex you can use this library:
https://gist.github.com/yi/01e3ab762838d567e65d
lockbox seems to have all the required functionality (aes128 and hmac).