The next step, is going to be a bit more complex as this is where the pairing occurs, and not being an expert in encryption/keys/pairing etc. I’m flying a bit blind with this…
Step 2 = with the pin displayed on the tv then call function authorise_pin_code(‘9712’) with the pin from the tv, that completes the pairing process, the tv will show message “pairing process complete”
My challenge here is to find comparable libraries/modules, as this part seems way more complex that the pin request part, as it has calls like these that I need to map/replace …
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.
lmcore.strtohex(decrypted)
lmcore.hextostr('15C95AC2B08AA7EB4E228F811E34D04FA54BA7DCAC9879FA8ACDA3FC244F3854', true)
aes_cbc:decrypt(encdec.base64dec(data))
aes_cbc:encrypt(payload)
encdec.hmacsha256(ciphertext, hmac_key, true)
encdec.base64enc(ciphertext .. sig)
Functions I’ve extracted based on the pairing process…
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 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 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>')
-- get AES and Hmac keys from x_keyword
sesh_key, sesh_hmac_key, sesh_iv = get_session_keys(enc_key)
-- request session key to be able to send encrypted commands
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)
--Set session ID and begin sequence number at 1. We have to increment the sequence number upon each successful NRC command.
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