Test device authentication
This commit is contained in:
parent
cf88642612
commit
3122f03e9e
9 changed files with 270 additions and 64 deletions
15
Cargo.toml
15
Cargo.toml
|
@ -8,15 +8,18 @@ edition = "2021"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = { version = "1.0.75", features = ["backtrace"] }
|
anyhow = { version = "1.0.75", features = ["backtrace"] }
|
||||||
if-addrs = "0.10.1"
|
if-addrs = "0.10.1"
|
||||||
aes = "0.8.3"
|
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
chrono = "0.4.31"
|
chrono = "0.4.31"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0.107"
|
serde_json = "1.0.107"
|
||||||
|
reqwest = "0.11.20"
|
||||||
|
tokio = { version = "1.32.0", features=["macros", "rt-multi-thread"] }
|
||||||
|
futures = "0.3.28"
|
||||||
|
|
||||||
|
#crypto
|
||||||
|
cbc = "0.1.2"
|
||||||
|
md5 = "0.7.0"
|
||||||
|
base64 = "0.21.4"
|
||||||
hmac = "0.12.1"
|
hmac = "0.12.1"
|
||||||
sha2 = "0.10.7"
|
sha2 = "0.10.7"
|
||||||
reqwest = "0.11.20"
|
aes = "0.8.3"
|
||||||
base64 = "0.21.4"
|
|
||||||
tokio = { version = "1.32.0", features=["macros", "rt-multi-thread"] }
|
|
||||||
md5 = "0.7.0"
|
|
||||||
futures = "0.3.28"
|
|
||||||
|
|
4
cloud.py
4
cloud.py
|
@ -73,8 +73,7 @@ class MideaCloud:
|
||||||
data.update({
|
data.update({
|
||||||
"stamp": datetime.datetime.now().strftime("%Y%m%d%H%M%S")
|
"stamp": datetime.datetime.now().strftime("%Y%m%d%H%M%S")
|
||||||
})
|
})
|
||||||
random = str(1695459986)
|
random = str(int(time.time()))
|
||||||
# random = str(int(time.time()))
|
|
||||||
url = self._api_url + endpoint
|
url = self._api_url + endpoint
|
||||||
dump_data = json.dumps(data)
|
dump_data = json.dumps(data)
|
||||||
sign = self._security.sign(dump_data, random)
|
sign = self._security.sign(dump_data, random)
|
||||||
|
@ -102,6 +101,7 @@ class MideaCloud:
|
||||||
pass
|
pass
|
||||||
if int(response["code"]) == 0 and "data" in response:
|
if int(response["code"]) == 0 and "data" in response:
|
||||||
return response["data"]
|
return response["data"]
|
||||||
|
print(response)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
async def _get_login_id(self) -> str | None:
|
async def _get_login_id(self) -> str | None:
|
||||||
|
|
|
@ -73,6 +73,11 @@ class MiedaDevice(threading.Thread):
|
||||||
self._heartbeat_interval = 10
|
self._heartbeat_interval = 10
|
||||||
self._default_refresh_interval = 30
|
self._default_refresh_interval = 30
|
||||||
|
|
||||||
|
print(token)
|
||||||
|
print(self._token)
|
||||||
|
|
||||||
|
k = 0
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
return self._device_name
|
return self._device_name
|
||||||
|
@ -146,6 +151,7 @@ class MiedaDevice(threading.Thread):
|
||||||
request = self._security.encode_8370(
|
request = self._security.encode_8370(
|
||||||
self._token, MSGTYPE_HANDSHAKE_REQUEST)
|
self._token, MSGTYPE_HANDSHAKE_REQUEST)
|
||||||
_LOGGER.debug(f"[{self._device_id}] Handshaking")
|
_LOGGER.debug(f"[{self._device_id}] Handshaking")
|
||||||
|
print(request)
|
||||||
self._socket.send(request)
|
self._socket.send(request)
|
||||||
response = self._socket.recv(512)
|
response = self._socket.recv(512)
|
||||||
if len(response) < 20:
|
if len(response) < 20:
|
||||||
|
|
|
@ -71,13 +71,6 @@ def discover(discover_type=None, ip_address=None):
|
||||||
encrypt_data = data[40:-16]
|
encrypt_data = data[40:-16]
|
||||||
reply = security.aes_decrypt(encrypt_data)
|
reply = security.aes_decrypt(encrypt_data)
|
||||||
_LOGGER.debug(f"Declassified reply: {reply.hex()}")
|
_LOGGER.debug(f"Declassified reply: {reply.hex()}")
|
||||||
|
|
||||||
start = 41
|
|
||||||
end = start + reply[40]
|
|
||||||
|
|
||||||
ssid_bytes = reply[start:end]
|
|
||||||
print(ssid_bytes)
|
|
||||||
|
|
||||||
ssid = reply[41:41 + reply[40]].decode("utf-8")
|
ssid = reply[41:41 + reply[40]].decode("utf-8")
|
||||||
device_type = ssid.split("_")[1]
|
device_type = ssid.split("_")[1]
|
||||||
port = bytes2port(reply[4:8])
|
port = bytes2port(reply[4:8])
|
||||||
|
|
63
midea.py
63
midea.py
|
@ -15,31 +15,54 @@ async def test():
|
||||||
|
|
||||||
devices = discover.discover()
|
devices = discover.discover()
|
||||||
|
|
||||||
if await cl.login():
|
if len(devices) > 0:
|
||||||
for device_id in devices:
|
for device_id in devices:
|
||||||
keys = await cl.get_keys(device_id)
|
token = "06df24fc4e8e950c6d9783051b8e38d971e5fbc617da259459d30d5e7d7fc05b4ccb708fe3a085f6f0af0f8cc961fa39dabfd0746f7bbcfbf7404d9cc5c2b077"
|
||||||
|
key = "2a5b5200c2c04d4c811d0550e1dc5b31435436b95b774d2a88d7e46d61fd9669"
|
||||||
|
|
||||||
for k in keys:
|
device_info = devices[device_id]
|
||||||
token = keys[k]['token']
|
|
||||||
key = keys[k]['key']
|
|
||||||
|
|
||||||
device_info = devices[device_id]
|
dev = device.MiedaDevice(
|
||||||
|
name="",
|
||||||
|
device_id=device_id,
|
||||||
|
device_type=225,
|
||||||
|
ip_address=device_info['ip_address'],
|
||||||
|
port=device_info['port'],
|
||||||
|
token=token,
|
||||||
|
key=key,
|
||||||
|
protocol=3,
|
||||||
|
model=device_info['model'],
|
||||||
|
attributes={}
|
||||||
|
)
|
||||||
|
|
||||||
dev = device.MiedaDevice(
|
dev.connect(False)
|
||||||
name="",
|
|
||||||
device_id=device_id,
|
|
||||||
device_type=225,
|
|
||||||
ip_address=device_info['ip_address'],
|
|
||||||
port=device_info['port'],
|
|
||||||
token=token,
|
|
||||||
key=key,
|
|
||||||
protocol=3,
|
|
||||||
model=device_info['model'],
|
|
||||||
attributes={}
|
|
||||||
)
|
|
||||||
|
|
||||||
if dev.connect(False):
|
|
||||||
return dev
|
# if await cl.login():
|
||||||
|
# for device_id in devices:
|
||||||
|
# keys = await cl.get_keys(device_id)
|
||||||
|
|
||||||
|
# for k in keys:
|
||||||
|
# token = keys[k]['token']
|
||||||
|
# key = keys[k]['key']
|
||||||
|
|
||||||
|
# device_info = devices[device_id]
|
||||||
|
|
||||||
|
# dev = device.MiedaDevice(
|
||||||
|
# name="",
|
||||||
|
# device_id=device_id,
|
||||||
|
# device_type=225,
|
||||||
|
# ip_address=device_info['ip_address'],
|
||||||
|
# port=device_info['port'],
|
||||||
|
# token=token,
|
||||||
|
# key=key,
|
||||||
|
# protocol=3,
|
||||||
|
# model=device_info['model'],
|
||||||
|
# attributes={}
|
||||||
|
# )
|
||||||
|
|
||||||
|
# if dev.connect(False):
|
||||||
|
# return dev
|
||||||
|
|
||||||
dev = asyncio.run(test())
|
dev = asyncio.run(test())
|
||||||
|
|
||||||
|
|
|
@ -38,9 +38,6 @@ class CloudSecurity:
|
||||||
return hex
|
return hex
|
||||||
|
|
||||||
def encrypt_password(self, login_id, data):
|
def encrypt_password(self, login_id, data):
|
||||||
login_id = "bd3937c1-49ba-418c-a6e0-4b600263"
|
|
||||||
data = "Hoda.semi1"
|
|
||||||
|
|
||||||
m = sha256()
|
m = sha256()
|
||||||
m.update(data.encode("ascii"))
|
m.update(data.encode("ascii"))
|
||||||
m_hex = m.hexdigest()
|
m_hex = m.hexdigest()
|
||||||
|
|
101
src/device.rs
101
src/device.rs
|
@ -1,8 +1,14 @@
|
||||||
use std::{net::UdpSocket, time::Duration};
|
use std::{
|
||||||
|
io::{Read, Write},
|
||||||
|
net::TcpStream,
|
||||||
|
thread,
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::{bail, Context, Error, Result};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
hex,
|
||||||
security::{MsgType, Security},
|
security::{MsgType, Security},
|
||||||
DeviceInfo,
|
DeviceInfo,
|
||||||
};
|
};
|
||||||
|
@ -10,22 +16,38 @@ use crate::{
|
||||||
pub struct Device {
|
pub struct Device {
|
||||||
info: DeviceInfo,
|
info: DeviceInfo,
|
||||||
|
|
||||||
socket: UdpSocket,
|
socket: TcpStream,
|
||||||
security: Security,
|
security: Security,
|
||||||
|
|
||||||
|
token: [u8; 64],
|
||||||
|
key: [u8; 32],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Device {
|
impl Device {
|
||||||
pub fn connect(info: DeviceInfo) -> Result<Self> {
|
pub fn connect(info: DeviceInfo, token: &str, key: &str) -> Result<Self> {
|
||||||
let socket = UdpSocket::bind("0.0.0.0:0")?;
|
let mut socket = Err(Error::msg(""));
|
||||||
|
|
||||||
|
for _ in 0..10 {
|
||||||
|
socket = TcpStream::connect(info.addr).context(info.addr);
|
||||||
|
|
||||||
|
if socket.is_ok() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
thread::sleep(Duration::from_millis(500));
|
||||||
|
}
|
||||||
|
|
||||||
|
let socket = socket?;
|
||||||
socket.set_write_timeout(Some(Duration::from_secs(10)))?;
|
socket.set_write_timeout(Some(Duration::from_secs(10)))?;
|
||||||
socket.set_read_timeout(Some(Duration::from_secs(10)))?;
|
socket.set_read_timeout(Some(Duration::from_secs(10)))?;
|
||||||
|
|
||||||
socket.connect(info.addr)?;
|
|
||||||
|
|
||||||
let mut me = Self {
|
let mut me = Self {
|
||||||
info,
|
info,
|
||||||
socket,
|
socket,
|
||||||
security: Security::default(),
|
security: Security::default(),
|
||||||
|
|
||||||
|
token: hex(token)?.try_into().unwrap(),
|
||||||
|
key: hex(key)?.try_into().unwrap(),
|
||||||
};
|
};
|
||||||
|
|
||||||
if me.info.protocol == 3 {
|
if me.info.protocol == 3 {
|
||||||
|
@ -38,7 +60,20 @@ impl Device {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn authenticate(&mut self) -> Result<()> {
|
fn authenticate(&mut self) -> Result<()> {
|
||||||
let request = self.security.encode_8370(MsgType::HANDSHAKE_REQUEST)?;
|
let request = self
|
||||||
|
.security
|
||||||
|
.encode_8370(&self.token, MsgType::HANDSHAKE_REQUEST)?;
|
||||||
|
|
||||||
|
self.socket.write(&request)?;
|
||||||
|
|
||||||
|
let mut buffer = [0; 512];
|
||||||
|
let bytes_read = self.socket.read(&mut buffer)?;
|
||||||
|
|
||||||
|
if bytes_read < 20 {
|
||||||
|
bail!("Authentication failed!");
|
||||||
|
}
|
||||||
|
|
||||||
|
self.security.tcp_key(&buffer[8..72], &self.key)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -52,16 +87,58 @@ impl Device {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use anyhow::Result;
|
use std::net::UdpSocket;
|
||||||
|
|
||||||
use crate::{device::Device, Startup};
|
use anyhow::Result;
|
||||||
|
use futures::future::try_join;
|
||||||
|
|
||||||
|
use crate::{device::Device, Cloud, Startup};
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn connect() -> Result<()> {
|
async fn connect() -> Result<()> {
|
||||||
for device_info in Startup::discover().await? {
|
let devices = Startup::discover().await?;
|
||||||
Device::connect(device_info)?;
|
|
||||||
|
const TOKEN:&str = "06df24fc4e8e950c6d9783051b8e38d971e5fbc617da259459d30d5e7d7fc05b4ccb708fe3a085f6f0af0f8cc961fa39dabfd0746f7bbcfbf7404d9cc5c2b077";
|
||||||
|
const KEY: &str = "2a5b5200c2c04d4c811d0550e1dc5b31435436b95b774d2a88d7e46d61fd9669";
|
||||||
|
|
||||||
|
let token_hex = b"\x06\xdf$\xfcN\x8e\x95\x0cm\x97\x83\x05\x1b\x8e8\xd9q\xe5\xfb\xc6\x17\xda%\x94Y\xd3\r^}\x7f\xc0[L\xcbp\x8f\xe3\xa0\x85\xf6\xf0\xaf\x0f\x8c\xc9a\xfa9\xda\xbf\xd0to{\xbc\xfb\xf7@M\x9c\xc5\xc2\xb0w";
|
||||||
|
let key_hex = b"*[R\x00\xc2\xc0ML\x81\x1d\x05P\xe1\xdc[1CT6\xb9[wM*\x88\xd7\xe4ma\xfd\x96i";
|
||||||
|
|
||||||
|
for device_info in devices {
|
||||||
|
let device = Device::connect(device_info, TOKEN, KEY)?;
|
||||||
|
|
||||||
|
assert_eq!(&device.token, token_hex);
|
||||||
|
assert_eq!(&device.key, key_hex);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn full_flow() -> Result<()> {
|
||||||
|
let mut cloud = Cloud::new("michaelh.95@t-online.de", "Hoda.semi1")?;
|
||||||
|
|
||||||
|
let (_, devices) = try_join(cloud.login(), Startup::discover()).await?;
|
||||||
|
|
||||||
|
for device_info in devices {
|
||||||
|
let (token, key) = cloud.keys(device_info.id).await?;
|
||||||
|
|
||||||
|
Device::connect(device_info, &token, &key)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn local_socket() -> Result<()> {
|
||||||
|
let socket = UdpSocket::bind("0.0.0.0:0")?;
|
||||||
|
socket.connect("192.168.178.94:6445")?;
|
||||||
|
|
||||||
|
socket.send(b"test")?;
|
||||||
|
|
||||||
|
let mut buffer = [0; 512];
|
||||||
|
let bytes_read = socket.recv(&mut buffer)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,7 @@ impl Startup {
|
||||||
socket.send_to(Self::BROADCAST_MSG, (addr, 20086))?;
|
socket.send_to(Self::BROADCAST_MSG, (addr, 20086))?;
|
||||||
|
|
||||||
let mut buffer = [0; 1024];
|
let mut buffer = [0; 1024];
|
||||||
if let Ok((bytes_read, addr)) = socket.recv_from(&mut buffer) {
|
if let Ok((bytes_read, mut addr)) = socket.recv_from(&mut buffer) {
|
||||||
let mut bytes = buffer[0..bytes_read].to_vec();
|
let mut bytes = buffer[0..bytes_read].to_vec();
|
||||||
|
|
||||||
let protocol = if bytes[..2] == hex("5a5a")? {
|
let protocol = if bytes[..2] == hex("5a5a")? {
|
||||||
|
@ -72,7 +72,7 @@ impl Startup {
|
||||||
}
|
}
|
||||||
|
|
||||||
let len = bytes.len();
|
let len = bytes.len();
|
||||||
let encrypt_data = Security::decrypt(&mut bytes[40..(len - 16)]);
|
let encrypt_data = Security::aes_decrypt(&mut bytes[40..(len - 16)]);
|
||||||
|
|
||||||
let start = 41;
|
let start = 41;
|
||||||
let upper = start + encrypt_data[40] as usize;
|
let upper = start + encrypt_data[40] as usize;
|
||||||
|
@ -81,6 +81,9 @@ impl Startup {
|
||||||
let device_type = u32::from_str_radix(ssid.split('_').nth(1).unwrap(), 16)?;
|
let device_type = u32::from_str_radix(ssid.split('_').nth(1).unwrap(), 16)?;
|
||||||
let model = from_utf8(&encrypt_data[17..25])?;
|
let model = from_utf8(&encrypt_data[17..25])?;
|
||||||
let sn = from_utf8(&encrypt_data[8..40])?;
|
let sn = from_utf8(&encrypt_data[8..40])?;
|
||||||
|
let port = Self::bytes_to_port(&encrypt_data[4..8]);
|
||||||
|
|
||||||
|
addr.set_port(port);
|
||||||
|
|
||||||
devices.insert(
|
devices.insert(
|
||||||
device_id,
|
device_id,
|
||||||
|
@ -111,6 +114,24 @@ impl Startup {
|
||||||
)
|
)
|
||||||
.collect())
|
.collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn bytes_to_port(bytes: &[u8]) -> u16 {
|
||||||
|
let mut b = 0;
|
||||||
|
let mut i: u32 = 0;
|
||||||
|
|
||||||
|
while b < 4 {
|
||||||
|
let b1 = if b < bytes.len() {
|
||||||
|
(bytes[b] & 0xFF) as u32
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
i |= b1 << b * 8;
|
||||||
|
b += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
i as u16
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Startup {
|
impl Startup {
|
||||||
|
|
110
src/security.rs
110
src/security.rs
|
@ -1,15 +1,19 @@
|
||||||
use aes::{
|
use aes::{
|
||||||
cipher::{generic_array::GenericArray, BlockDecrypt, KeyInit},
|
cipher::{
|
||||||
|
block_padding::Pkcs7, generic_array::GenericArray, BlockDecrypt, BlockDecryptMut,
|
||||||
|
BlockEncryptMut, KeyInit, KeyIvInit,
|
||||||
|
},
|
||||||
Aes128,
|
Aes128,
|
||||||
};
|
};
|
||||||
use anyhow::Result;
|
use anyhow::{bail, Result};
|
||||||
use rand::{self, RngCore};
|
use rand::{self, RngCore};
|
||||||
|
use sha2::{Digest, Sha256};
|
||||||
|
|
||||||
use crate::hex;
|
use crate::hex;
|
||||||
|
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
|
||||||
pub enum MsgType {
|
pub enum MsgType {
|
||||||
HANDSHAKE_REQUEST = 0x0,
|
HANDSHAKE_REQUEST = 0x0,
|
||||||
ANDSHAKE_RESPONSE = 0x1,
|
ANDSHAKE_RESPONSE = 0x1,
|
||||||
|
@ -19,15 +23,18 @@ pub enum MsgType {
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Security {
|
pub struct Security {
|
||||||
request_count: u32,
|
request_count: u16,
|
||||||
response_count: u32,
|
response_count: u16,
|
||||||
|
|
||||||
|
tcp_key: Option<[u8; 32]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Security {
|
impl Security {
|
||||||
const N: u128 = 141661095494369103254425781617665632877;
|
const N: u128 = 141661095494369103254425781617665632877;
|
||||||
const KEY: [u8; 16] = Self::N.to_be_bytes();
|
const KEY: [u8; 16] = Self::N.to_be_bytes();
|
||||||
|
const IV: [u8; 16] = [b'\0'; 16];
|
||||||
|
|
||||||
pub fn decrypt(data: &mut [u8]) -> &[u8] {
|
pub fn aes_decrypt(data: &mut [u8]) -> &[u8] {
|
||||||
let array = GenericArray::from(Self::KEY);
|
let array = GenericArray::from(Self::KEY);
|
||||||
let cipher = Aes128::new(&array);
|
let cipher = Aes128::new(&array);
|
||||||
|
|
||||||
|
@ -39,11 +46,64 @@ impl Security {
|
||||||
data
|
data
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn encode_8370(&mut self, msg_type: MsgType) -> Result<String> {
|
pub fn aes_cbc_encrypt(&self, raw: &[u8], key: &[u8; 32]) -> Vec<u8> {
|
||||||
let mut header = hex("83,70")?;
|
type Aes256CbcEnc = cbc::Encryptor<aes::Aes256>;
|
||||||
let mut data: Vec<u8> = Vec::new();
|
|
||||||
|
|
||||||
let mut size = data.len();
|
let mut buf = vec![0; raw.len()];
|
||||||
|
buf.copy_from_slice(raw);
|
||||||
|
|
||||||
|
Aes256CbcEnc::new(key.into(), &Self::IV.into())
|
||||||
|
.encrypt_padded_mut::<Pkcs7>(&mut buf, raw.len())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
buf
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn aes_cbc_decrypt(&self, raw: &[u8], key: &[u8; 32]) -> Vec<u8> {
|
||||||
|
type Aes256CbcDec = cbc::Decryptor<aes::Aes256>;
|
||||||
|
|
||||||
|
let mut buf = vec![0; raw.len()];
|
||||||
|
buf.copy_from_slice(raw);
|
||||||
|
|
||||||
|
Aes256CbcDec::new(key.into(), &Self::IV.into())
|
||||||
|
.decrypt_padded_mut::<Pkcs7>(&mut buf)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
buf
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tcp_key(&mut self, response: &[u8], key: &[u8; 32]) -> Result<()> {
|
||||||
|
if response == b"ERROR" {
|
||||||
|
bail!("authentication failed!");
|
||||||
|
}
|
||||||
|
|
||||||
|
let payload = &response[0..32];
|
||||||
|
let sign = &response[32..];
|
||||||
|
|
||||||
|
let plain = self.aes_cbc_decrypt(payload, &key);
|
||||||
|
|
||||||
|
if Sha256::digest(&plain).into_iter().collect::<Vec<u8>>() != sign {
|
||||||
|
bail!("sign does not match");
|
||||||
|
}
|
||||||
|
|
||||||
|
self.tcp_key = Some(Self::xorstr(&plain, key).try_into().unwrap());
|
||||||
|
self.request_count = 0;
|
||||||
|
self.response_count = 0;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn xorstr(lhs: &[u8], rhs: &[u8]) -> Vec<u8> {
|
||||||
|
assert_eq!(lhs.len(), rhs.len());
|
||||||
|
|
||||||
|
lhs.iter().zip(rhs.iter()).map(|(&l, &r)| l ^ r).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode_8370(&mut self, msg: &[u8], msg_type: MsgType) -> Result<Vec<u8>> {
|
||||||
|
let mut header = hex("8370")?;
|
||||||
|
let mut data: Vec<u8> = msg.to_vec();
|
||||||
|
|
||||||
|
let mut size = data.len() as u16;
|
||||||
let mut padding = 0;
|
let mut padding = 0;
|
||||||
|
|
||||||
if msg_type == MsgType::ENCRYPTED_RESPONSE || msg_type == MsgType::ENCRYPTED_REQUEST {
|
if msg_type == MsgType::ENCRYPTED_RESPONSE || msg_type == MsgType::ENCRYPTED_REQUEST {
|
||||||
|
@ -51,7 +111,7 @@ impl Security {
|
||||||
padding = 16 - ((size + 2) & 0xf);
|
padding = 16 - ((size + 2) & 0xf);
|
||||||
size += padding + 32;
|
size += padding + 32;
|
||||||
data.extend({
|
data.extend({
|
||||||
let mut d = vec![0; padding];
|
let mut d = vec![0; padding as usize];
|
||||||
rand::thread_rng().fill_bytes(&mut d);
|
rand::thread_rng().fill_bytes(&mut d);
|
||||||
|
|
||||||
d
|
d
|
||||||
|
@ -59,6 +119,32 @@ impl Security {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
todo!()
|
header.extend(size.to_be_bytes());
|
||||||
|
header.extend([0x20, (padding << 4) as u8 | msg_type as u8]);
|
||||||
|
|
||||||
|
data = {
|
||||||
|
let mut b = self.request_count.to_be_bytes().to_vec();
|
||||||
|
b.extend(data);
|
||||||
|
b
|
||||||
|
};
|
||||||
|
|
||||||
|
(self.request_count, _) = self.request_count.overflowing_add(1);
|
||||||
|
|
||||||
|
if msg_type == MsgType::ENCRYPTED_RESPONSE || msg_type == MsgType::ENCRYPTED_REQUEST {
|
||||||
|
let sign: Vec<u8> = Sha256::digest(Self::add_bytes(header.clone(), data.clone()))
|
||||||
|
.into_iter()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
data = self.aes_cbc_encrypt(&data, self.tcp_key.as_ref().unwrap());
|
||||||
|
data.extend(sign);
|
||||||
|
}
|
||||||
|
|
||||||
|
header.extend(data);
|
||||||
|
Ok(header)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_bytes(mut lhs: Vec<u8>, rhs: Vec<u8>) -> Vec<u8> {
|
||||||
|
lhs.extend(rhs);
|
||||||
|
lhs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue