Implement 8370 message encryption

This commit is contained in:
hodasemi 2023-10-02 09:30:58 +02:00
parent d1bef25ac9
commit 2aec54c7a7
7 changed files with 140 additions and 40 deletions

View file

@ -171,12 +171,26 @@ class MiedaDevice(threading.Thread):
def send_message_v3(self, data, msg_type=MSGTYPE_ENCRYPTED_REQUEST): def send_message_v3(self, data, msg_type=MSGTYPE_ENCRYPTED_REQUEST):
data = self._security.encode_8370(data, msg_type) data = self._security.encode_8370(data, msg_type)
print("=== encrypted ===")
print(data)
self.send_message_v2(data) self.send_message_v2(data)
def build_send(self, cmd): def build_send(self, cmd):
data = cmd.serialize() data = cmd.serialize()
print("=== data ===")
print(data)
print
_LOGGER.debug(f"[{self._device_id}] Sending: {cmd}") _LOGGER.debug(f"[{self._device_id}] Sending: {cmd}")
msg = PacketBuilder(self._device_id, data).finalize() msg = PacketBuilder(self._device_id, data).finalize()
print("=== msg ===")
print(msg)
print
self.send_message(msg) self.send_message(msg)
def refresh_status(self, wait_response=False): def refresh_status(self, wait_response=False):

View file

@ -71,31 +71,6 @@ async def test():
if dev.connect(True): if dev.connect(True):
return dev return dev
t = datetime.datetime.now().strftime("%Y%m%d%H%M%S%f")[:16]
t = "20230929142324212188673"[:16]
print(t)
b = bytearray()
for i in range(0, len(t), 2):
tmp = t[i:i+2]
d = int(tmp)
b.insert(0, d)
print(b)
b_ref = bytearray([21, 24, 23, 14, 29, 9, 23, 20])
print(b_ref)
dev = asyncio.run(test()) dev = asyncio.run(test())
print(dev) print(dev)

View file

@ -6,6 +6,9 @@ pub trait RequestSerializer {
} }
pub struct CommandRequest { pub struct CommandRequest {
#[cfg(debug_assertions)]
pub name: &'static str,
command: Command, command: Command,
} }
@ -19,6 +22,7 @@ impl CommandRequest {
device_type: u8, device_type: u8,
message_type: MessageType, message_type: MessageType,
body: Body, body: Body,
#[cfg(debug_assertions)] name: &'static str,
) -> Self { ) -> Self {
Self { Self {
command: Command { command: Command {
@ -30,6 +34,8 @@ impl CommandRequest {
), ),
body, body,
}, },
name,
} }
} }
@ -69,7 +75,9 @@ impl CommandQuerySubtype {
0, 0,
device_type, device_type,
MessageType::QuerySubtype, MessageType::QuerySubtype,
Body::from([0x00; 18].as_slice()), Body::from(([0x00; 18].as_slice(), 0x00)),
#[cfg(debug_assertions)]
"QuerySubtype",
), ),
} }
} }
@ -100,7 +108,14 @@ pub struct CommandQueryCustom {
impl CommandQueryCustom { impl CommandQueryCustom {
pub fn new(device_type: u8, message_type: MessageType, body: Body) -> Self { pub fn new(device_type: u8, message_type: MessageType, body: Body) -> Self {
Self { Self {
command: CommandRequest::new(0, device_type, message_type, body), command: CommandRequest::new(
0,
device_type,
message_type,
body,
#[cfg(debug_assertions)]
"QueryCustom",
),
} }
} }

View file

@ -151,7 +151,7 @@ impl Device {
} }
fn parse_message(&mut self, msg: &[u8]) -> Result<ParseMessage> { fn parse_message(&mut self, msg: &[u8]) -> Result<ParseMessage> {
let (messages, buffer) = self.security.decode_8370(&self.buffer, msg)?; let (messages, buffer) = self.security.decode_8370([&self.buffer, msg].concat())?;
self.buffer = buffer; self.buffer = buffer;
if messages.is_empty() { if messages.is_empty() {
@ -207,6 +207,12 @@ impl Device {
fn send_message(&self, msg: &[u8]) -> Result<()> { fn send_message(&self, msg: &[u8]) -> Result<()> {
let data = self.security.encode_8370(msg, MsgType::ENCRYPTED_REQUEST)?; let data = self.security.encode_8370(msg, MsgType::ENCRYPTED_REQUEST)?;
// encrypted
let e = b"\x83p\x00~ fq\xa8\xa9b(\x8c\r,\x96X\xbdT\x1d\x06\xa1/\xb5@\xa2\xeb\x96\x0c\x01s\xf0\x8c\x98ELT\x89\x81\xcc\x9d\xaa\xb6[dq\xcf\x98\xd1s\x8c\x08\x0e\xe6u1D\x80\x17I\xe2\x987s\xbe\xb2\xa9\x13\x86\xce\xb3qq\xfe\xa6\x11\x10\xcfi\xc2\xaeXJH\xb8\xa8\x0b5\x08z\x00\xec\xa2t\x13\xeds\xe7:\x0f\x0eP\xfe\x80w7\xbb\xdf\x0f\x14D\xfd9\xceZ\xda\x1a\xda\xfb\x0b\xe0\x92\xc2D\xb4\xdfWE\x89_\xd9\xd0\xb2\xb6\xd9";
// assert_eq!(data, e);
self.socket.lock().unwrap().write(&data)?; self.socket.lock().unwrap().write(&data)?;
Ok(()) Ok(())
@ -219,6 +225,12 @@ impl Device {
fn build_send(&self, cmd: impl RequestSerializer) -> Result<()> { fn build_send(&self, cmd: impl RequestSerializer) -> Result<()> {
let data = PacketBuilder::builder(self.info.id, cmd).finalize(1); let data = PacketBuilder::builder(self.info.id, cmd).finalize(1);
// msg
let d = b"ZZ\x01\x11X\x00 \x00\x00\x00\x00\x00\x0435\x06\x02\n\x17\x14\x02\x86\x02\x00\x00\x8b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x17G\x0b\xc3\xe7\r\xaa\xb93\x81r0?\x02s\xf8\x88\xe9G)\xa5\x127\x99\xdd\x8afi\xbd\xf6\xc3\xf5\xf1B\x84\x8a&n\xd9\xdb\xba\xee(\\\'\xb2\x0c\x02";
// assert_eq!(data, d);
self.send_message(&data) self.send_message(&data)
} }
} }

View file

@ -293,9 +293,14 @@ pub struct CommandE1Base {
} }
impl CommandE1Base { impl CommandE1Base {
pub fn new(device_protocol_version: u8, message_type: MessageType, body: Body) -> Self { pub fn new(
device_protocol_version: u8,
message_type: MessageType,
body: Body,
#[cfg(debug_assertions)] name: &'static str,
) -> Self {
Self { Self {
command: CommandRequest::new(device_protocol_version, 0xE1, message_type, body), command: CommandRequest::new(device_protocol_version, 0xE1, message_type, body, name),
} }
} }
} }
@ -325,6 +330,8 @@ impl CommandPower {
device_protocol_version, device_protocol_version,
MessageType::Set, MessageType::Set,
Body::from(([0x00; 4].as_slice(), 0x08)), Body::from(([0x00; 4].as_slice(), 0x08)),
#[cfg(debug_assertions)]
"Power",
), ),
} }
} }
@ -361,6 +368,8 @@ impl CommandLock {
device_protocol_version, device_protocol_version,
MessageType::Set, MessageType::Set,
Body::from((concat!(vec![0x04], vec![0x00; 36],).as_slice(), 0x08)), Body::from((concat!(vec![0x04], vec![0x00; 36],).as_slice(), 0x08)),
#[cfg(debug_assertions)]
"Lock",
), ),
} }
} }
@ -400,6 +409,8 @@ impl CommandStorage {
concat!(vec![0x00; 4], vec![0xFF; 6], vec![0x00; 27],).as_slice(), concat!(vec![0x00; 4], vec![0xFF; 6], vec![0x00; 27],).as_slice(),
0x81, 0x81,
)), )),
#[cfg(debug_assertions)]
"Storage",
), ),
} }
} }
@ -436,6 +447,8 @@ impl CommandQuery {
device_protocol_version, device_protocol_version,
MessageType::Query, MessageType::Query,
Body::from(([].as_slice(), 0x00)), Body::from(([].as_slice(), 0x00)),
#[cfg(debug_assertions)]
"Query",
), ),
} }
} }

View file

@ -63,6 +63,12 @@ impl<S: RequestSerializer> PacketBuilder<S> {
packet[6] = 0x7b; packet[6] = 0x7b;
} else { } else {
let mut data = self.command.serialize(); let mut data = self.command.serialize();
// data
let d = b"\xaa\x1d\xe1\x00\x00\x00\x00\x00\x00\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00b";
assert_eq!(data, d);
Security::aes_encrypt(&mut data); Security::aes_encrypt(&mut data);
packet.extend(data); packet.extend(data);

View file

@ -74,13 +74,18 @@ impl Security {
} }
} }
pub fn aes_cbc_encrypt(&self, raw: [u8; 32], key: &[u8; 32]) -> [u8; 32] { pub fn aes_cbc_encrypt(&self, raw: &[u8], key: &[u8; 32]) -> Vec<u8> {
type Aes256CbcEnc = cbc::Encryptor<aes::Aes256>; type Aes256CbcEnc = cbc::Encryptor<aes::Aes256>;
debug_assert_eq!(raw.len() % key.len(), 0);
raw.chunks(key.len())
.map(|r| {
Aes256CbcEnc::new(key.into(), &Self::IV.into()) Aes256CbcEnc::new(key.into(), &Self::IV.into())
.encrypt_padded_vec_mut::<NoPadding>(&raw) .encrypt_padded_vec_mut::<NoPadding>(r)
.try_into() })
.unwrap() .flatten()
.collect()
} }
pub fn aes_cbc_decrypt(&self, raw: [u8; 32], key: &[u8; 32]) -> [u8; 32] { pub fn aes_cbc_decrypt(&self, raw: [u8; 32], key: &[u8; 32]) -> [u8; 32] {
@ -163,9 +168,7 @@ impl Security {
.into_iter() .into_iter()
.collect(); .collect();
data = self data = self.aes_cbc_encrypt(&data, self.tcp_key.as_ref().unwrap());
.aes_cbc_encrypt(data.try_into().unwrap(), self.tcp_key.as_ref().unwrap())
.to_vec();
data.extend(sign); data.extend(sign);
} }
@ -178,8 +181,70 @@ impl Security {
lhs lhs
} }
pub fn decode_8370(&self, buffer: &[u8], msg: &[u8]) -> Result<(Vec<Vec<u8>>, Vec<u8>)> { pub fn decode_8370(&self, mut data: Vec<u8>) -> Result<(Vec<Vec<u8>>, Vec<u8>)> {
todo!() if data.len() < 6 {
return Ok((Vec::new(), data));
}
let header = data[..6].to_vec();
if header[0] != 0x83 || header[1] != 0x70 {
bail!("no an 8370 message");
} else if header[4] != 0x20 {
bail!("missing byte 4");
}
let size = u16::from_be_bytes(header[2..4].try_into().unwrap()) as usize + 8;
let mut leftover = Vec::new();
if data.len() < size {
return Ok((Vec::new(), data.to_vec()));
} else if data.len() > size {
leftover = data[size..].to_vec();
data = data[..size].to_vec();
}
let padding = header[5] >> 4;
let msgtype = header[5] & 0xf;
data = data[6..].to_vec();
if msgtype == MsgType::ENCRYPTED_RESPONSE as u8
|| msgtype == MsgType::ENCRYPTED_REQUEST as u8
{
let sign = data[(data.len() - 32)..].to_vec();
data = data[..(data.len() - 32)].to_vec();
data = self
.aes_cbc_decrypt(data.try_into().unwrap(), &self.tcp_key.unwrap())
.to_vec();
let compare_sign: Vec<u8> = Sha256::digest(&[header.to_vec(), data.clone()].concat())
.into_iter()
.collect();
if sign != compare_sign {
bail!("sign does not match");
}
if padding != 0 {
data = data[..(data.len() - padding as usize)].to_vec();
}
}
self.response_count
.store(u16::from_be_bytes(data[..2].try_into().unwrap()), SeqCst);
data = data[2..].to_vec();
if leftover.len() > 0 {
let (mut packets, incomplete) = self.decode_8370(leftover)?;
packets.insert(0, data);
Ok((packets, incomplete))
} else {
Ok((vec![data], Vec::new()))
}
} }
} }