Fix connection to device
This commit is contained in:
parent
2aec54c7a7
commit
1268d2df99
7 changed files with 276 additions and 402 deletions
68
midea.py
68
midea.py
|
@ -9,69 +9,67 @@ import packet_builder;
|
||||||
|
|
||||||
import datetime;
|
import datetime;
|
||||||
|
|
||||||
async def test():
|
# async def test():
|
||||||
cl = cloud.MSmartHomeCloud(
|
# cl = cloud.MSmartHomeCloud(
|
||||||
"MSmartHome",
|
# "MSmartHome",
|
||||||
aiohttp.ClientSession(),
|
# aiohttp.ClientSession(),
|
||||||
"michaelh.95@t-online.de",
|
# "michaelh.95@t-online.de",
|
||||||
"Hoda.semi1"
|
# "Hoda.semi1"
|
||||||
)
|
# )
|
||||||
|
|
||||||
devices = discover.discover()
|
# devices = discover.discover()
|
||||||
|
|
||||||
if len(devices) > 0:
|
# if len(devices) > 0:
|
||||||
|
# if await cl.login():
|
||||||
# for device_id in devices:
|
# for device_id in devices:
|
||||||
# token = "702b9dfc3ac6c82979986ee3a053a76f75f9e9c763ce5c25af5c2cc982f797a9409adff3745e23fee3a464d745e005c839efb0b84082acc962e59ab8683e0299"
|
# keys = await cl.get_keys(device_id)
|
||||||
# key = "52b2feee353841588994e630dcb59819ec71ce1ffacb48628f4f436f5c54f11e"
|
|
||||||
|
# for k in keys:
|
||||||
|
# token = keys[k]['token']
|
||||||
|
# key = keys[k]['key']
|
||||||
|
|
||||||
# device_info = devices[device_id]
|
# device_info = devices[device_id]
|
||||||
|
|
||||||
# dev = device.MiedaDevice(
|
# dev = devicee1.MideaE1Device(
|
||||||
# name="",
|
# name="",
|
||||||
# device_id=device_id,
|
# device_id=device_id,
|
||||||
# device_type=225,
|
|
||||||
# ip_address=device_info['ip_address'],
|
# ip_address=device_info['ip_address'],
|
||||||
# port=device_info['port'],
|
# port=device_info['port'],
|
||||||
# token=token,
|
# token=token,
|
||||||
# key=key,
|
# key=key,
|
||||||
# protocol=3,
|
# protocol=3,
|
||||||
# model=device_info['model'],
|
# model=device_info['model'],
|
||||||
# attributes={}
|
# customize=""
|
||||||
# )
|
# )
|
||||||
|
|
||||||
# if dev.connect(False):
|
# if dev.connect(True):
|
||||||
# print("success")
|
# return dev
|
||||||
# else:
|
|
||||||
# print("fail")
|
|
||||||
|
|
||||||
|
key = b"V\x103\xba\xa0W\x85\xaa\x0c\x01q\xb7\x94\t\x7f\xd4\xe7=L\x91\x02\x14\x8d`\xf7~\xc6\xfd\x99?\x14\xbc"
|
||||||
|
|
||||||
|
chunk = bytearray([77, 15, 242, 219, 218, 33, 185, 171, 8, 35, 207, 179, 31, 179, 75, 165])
|
||||||
|
chunk_ref = bytearray([49, 19, 2, 10, 23, 20, 2, 134, 2, 0, 0, 139, 0, 0, 0, 0])
|
||||||
|
|
||||||
if await cl.login():
|
sec = security.LocalSecurity()
|
||||||
for device_id in devices:
|
chunk_res = sec.aes_cbc_decrypt(chunk, key)
|
||||||
keys = await cl.get_keys(device_id)
|
|
||||||
|
|
||||||
for k in keys:
|
print(chunk_ref)
|
||||||
token = keys[k]['token']
|
print(chunk_res)
|
||||||
key = keys[k]['key']
|
|
||||||
|
|
||||||
device_info = devices[device_id]
|
|
||||||
|
|
||||||
dev = devicee1.MideaE1Device(
|
dev = devicee1.MideaE1Device(
|
||||||
name="",
|
name="",
|
||||||
device_id=device_id,
|
device_id=152832116426242,
|
||||||
ip_address=device_info['ip_address'],
|
ip_address="192.168.178.94",
|
||||||
port=device_info['port'],
|
port=6444,
|
||||||
token=token,
|
token="dead840607856a4b84c1a9d94ce8f553b50c037b65fa1ca22126de339c367eb765b231b4525d5b8336c48fe5dae38439bbb5e31282ed3790ff98a48049401dca",
|
||||||
key=key,
|
key="50e77947dc63426db3883c7616613410044b02567f5240a5baf789391b2e5a79",
|
||||||
protocol=3,
|
protocol=3,
|
||||||
model=device_info['model'],
|
model="760EY015",
|
||||||
customize=""
|
customize=""
|
||||||
)
|
)
|
||||||
|
|
||||||
if dev.connect(True):
|
dev.connect(True)
|
||||||
return dev
|
|
||||||
|
|
||||||
dev = asyncio.run(test())
|
|
||||||
|
|
||||||
print(dev)
|
print(dev)
|
||||||
|
|
||||||
|
|
|
@ -250,6 +250,9 @@ class LocalSecurity:
|
||||||
padding = header[5] >> 4
|
padding = header[5] >> 4
|
||||||
msgtype = header[5] & 0xf
|
msgtype = header[5] & 0xf
|
||||||
data = data[6:]
|
data = data[6:]
|
||||||
|
|
||||||
|
# (6) vec![131, 112, 0, 5, 32, 15]
|
||||||
|
|
||||||
if msgtype in (MSGTYPE_ENCRYPTED_RESPONSE, MSGTYPE_ENCRYPTED_REQUEST):
|
if msgtype in (MSGTYPE_ENCRYPTED_RESPONSE, MSGTYPE_ENCRYPTED_REQUEST):
|
||||||
sign = data[-32:]
|
sign = data[-32:]
|
||||||
data = data[:-32]
|
data = data[:-32]
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use std::{
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
io::{Read, Write},
|
io::{Read, Write},
|
||||||
net::TcpStream,
|
net::TcpStream,
|
||||||
sync::Mutex,
|
sync::Mutex,
|
||||||
|
@ -10,10 +11,10 @@ use anyhow::{bail, Context, Error, Result};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
command::{
|
command::{
|
||||||
CommandHeartbeat, CommandQuerySubtype, CommandRequest, CommandSubtypeResponse, MessageType,
|
CommandHeartbeat, CommandQuerySubtype, CommandSubtypeResponse, MessageType,
|
||||||
RequestSerializer,
|
RequestSerializer,
|
||||||
},
|
},
|
||||||
devices::{e1::E1, DeviceBackend},
|
devices::{e1::E1, AttributeValue, DeviceBackend},
|
||||||
hex,
|
hex,
|
||||||
packet_builder::PacketBuilder,
|
packet_builder::PacketBuilder,
|
||||||
security::{MsgType, Security},
|
security::{MsgType, Security},
|
||||||
|
@ -37,7 +38,7 @@ pub struct Device {
|
||||||
sub_type: u16,
|
sub_type: u16,
|
||||||
device_protocol_version: u8,
|
device_protocol_version: u8,
|
||||||
|
|
||||||
updates: Vec<Box<dyn Fn(&[u8]) -> Result<()>>>,
|
updates: Vec<Box<dyn Fn(&HashMap<&'static str, AttributeValue>) -> Result<()>>>,
|
||||||
|
|
||||||
token: [u8; 64],
|
token: [u8; 64],
|
||||||
key: [u8; 32],
|
key: [u8; 32],
|
||||||
|
@ -127,7 +128,10 @@ impl Device {
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let mut buf = [0; 512];
|
let mut buf = [0; 512];
|
||||||
let bytes_read = self.socket.lock().unwrap().read(&mut buf)?;
|
let bytes_read = match self.socket.lock().unwrap().read(&mut buf) {
|
||||||
|
Ok(b) => b,
|
||||||
|
Err(_) => break,
|
||||||
|
};
|
||||||
|
|
||||||
if bytes_read == 0 {
|
if bytes_read == 0 {
|
||||||
bail!("socket error");
|
bail!("socket error");
|
||||||
|
@ -145,7 +149,7 @@ impl Device {
|
||||||
|
|
||||||
pub fn register_update<F>(&mut self, f: F)
|
pub fn register_update<F>(&mut self, f: F)
|
||||||
where
|
where
|
||||||
F: Fn(&[u8]) -> Result<()> + 'static,
|
F: Fn(&HashMap<&'static str, AttributeValue>) -> Result<()> + 'static,
|
||||||
{
|
{
|
||||||
self.updates.push(Box::new(f));
|
self.updates.push(Box::new(f));
|
||||||
}
|
}
|
||||||
|
@ -178,12 +182,10 @@ impl Device {
|
||||||
Security::aes_decrypt(crypt);
|
Security::aes_decrypt(crypt);
|
||||||
|
|
||||||
if self.pre_process_message(crypt) {
|
if self.pre_process_message(crypt) {
|
||||||
let status = self.device_backend.process_message(crypt)?;
|
self.device_backend.process_message(crypt);
|
||||||
|
|
||||||
if status.len() > 0 {
|
|
||||||
for update in self.updates.iter() {
|
for update in self.updates.iter() {
|
||||||
update(&status)?;
|
update(self.device_backend.attributes())?;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -208,11 +210,6 @@ 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(())
|
||||||
|
@ -226,17 +223,14 @@ 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
use std::net::{Ipv4Addr, SocketAddr};
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use futures::future::try_join;
|
use futures::future::try_join;
|
||||||
use serial_test::serial;
|
use serial_test::serial;
|
||||||
|
@ -293,4 +287,24 @@ mod test {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
#[serial]
|
||||||
|
async fn communication() -> Result<()> {
|
||||||
|
let token = "dead840607856a4b84c1a9d94ce8f553b50c037b65fa1ca22126de339c367eb765b231b4525d5b8336c48fe5dae38439bbb5e31282ed3790ff98a48049401dca";
|
||||||
|
let key = "50e77947dc63426db3883c7616613410044b02567f5240a5baf789391b2e5a79";
|
||||||
|
|
||||||
|
let device_info = crate::DeviceInfo {
|
||||||
|
id: 152832116426242,
|
||||||
|
model: "760EY015".to_string(),
|
||||||
|
sn: "0000E1541760EY01534091D002581H2R".to_string(),
|
||||||
|
protocol: 3,
|
||||||
|
device_type: 225,
|
||||||
|
addr: SocketAddr::new(std::net::IpAddr::V4(Ipv4Addr::new(192, 168, 178, 94)), 6444),
|
||||||
|
};
|
||||||
|
|
||||||
|
Device::connect(device_info, token, key)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,166 +7,11 @@ use anyhow::Result;
|
||||||
|
|
||||||
use crate::command::*;
|
use crate::command::*;
|
||||||
|
|
||||||
use super::DeviceBackend;
|
use super::{AttributeValue, DeviceBackend};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
||||||
pub enum DeviceAttributes {
|
|
||||||
Power,
|
|
||||||
Status,
|
|
||||||
Mode,
|
|
||||||
Additional,
|
|
||||||
Door,
|
|
||||||
RinseAid,
|
|
||||||
Salt,
|
|
||||||
ChildLock,
|
|
||||||
UV,
|
|
||||||
Dry,
|
|
||||||
DryStatus,
|
|
||||||
Storage,
|
|
||||||
StorageStatus,
|
|
||||||
TimeRemaining,
|
|
||||||
Progress,
|
|
||||||
StorageRemaining,
|
|
||||||
Temperature,
|
|
||||||
Humidity,
|
|
||||||
Waterswitch,
|
|
||||||
WaterLack,
|
|
||||||
ErrorCode,
|
|
||||||
Softwater,
|
|
||||||
WrongOperation,
|
|
||||||
Bright,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DeviceAttributes {
|
|
||||||
fn as_str(&self) -> &str {
|
|
||||||
match self {
|
|
||||||
DeviceAttributes::Power => "power",
|
|
||||||
DeviceAttributes::Status => "status",
|
|
||||||
DeviceAttributes::Mode => "mode",
|
|
||||||
DeviceAttributes::Additional => "additional",
|
|
||||||
DeviceAttributes::Door => "door",
|
|
||||||
DeviceAttributes::RinseAid => "rinse_aid",
|
|
||||||
DeviceAttributes::Salt => "salt",
|
|
||||||
DeviceAttributes::ChildLock => "child_lock",
|
|
||||||
DeviceAttributes::UV => "uv",
|
|
||||||
DeviceAttributes::Dry => "dry",
|
|
||||||
DeviceAttributes::DryStatus => "dry_status",
|
|
||||||
DeviceAttributes::Storage => "storage",
|
|
||||||
DeviceAttributes::StorageStatus => "storage_status",
|
|
||||||
DeviceAttributes::TimeRemaining => "time_remaining",
|
|
||||||
DeviceAttributes::Progress => "progress",
|
|
||||||
DeviceAttributes::StorageRemaining => "storage_remaining",
|
|
||||||
DeviceAttributes::Temperature => "temperature",
|
|
||||||
DeviceAttributes::Humidity => "humidity",
|
|
||||||
DeviceAttributes::Waterswitch => "waterswitch",
|
|
||||||
DeviceAttributes::WaterLack => "water_lack",
|
|
||||||
DeviceAttributes::ErrorCode => "error_code",
|
|
||||||
DeviceAttributes::Softwater => "softwater",
|
|
||||||
DeviceAttributes::WrongOperation => "wrong_operation",
|
|
||||||
DeviceAttributes::Bright => "bright",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> Self {
|
|
||||||
match s {
|
|
||||||
"power" => Self::Power,
|
|
||||||
"status" => Self::Status,
|
|
||||||
"mode" => Self::Mode,
|
|
||||||
"additional" => Self::Additional,
|
|
||||||
"door" => Self::Door,
|
|
||||||
"rinse_aid" => Self::RinseAid,
|
|
||||||
"salt" => Self::Salt,
|
|
||||||
"child_lock" => Self::ChildLock,
|
|
||||||
"uv" => Self::UV,
|
|
||||||
"dry" => Self::Dry,
|
|
||||||
"dry_status" => Self::DryStatus,
|
|
||||||
"storage" => Self::Storage,
|
|
||||||
"storage_status" => Self::StorageStatus,
|
|
||||||
"time_remaining" => Self::TimeRemaining,
|
|
||||||
"progress" => Self::Progress,
|
|
||||||
"storage_remaining" => Self::StorageRemaining,
|
|
||||||
"temperature" => Self::Temperature,
|
|
||||||
"humidity" => Self::Humidity,
|
|
||||||
"waterswitch" => Self::Waterswitch,
|
|
||||||
"water_lack" => Self::WaterLack,
|
|
||||||
"error_code" => Self::ErrorCode,
|
|
||||||
"softwater" => Self::Softwater,
|
|
||||||
"wrong_operation" => Self::WrongOperation,
|
|
||||||
"bright" => Self::Bright,
|
|
||||||
|
|
||||||
_ => panic!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum AttributeValue {
|
|
||||||
String(Option<String>),
|
|
||||||
Bool(bool),
|
|
||||||
Int(u8),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AttributeValue {
|
|
||||||
pub fn set(&mut self, value: impl Into<Self>) {
|
|
||||||
match (self, value.into()) {
|
|
||||||
(Self::String(current), Self::String(new)) => *current = new,
|
|
||||||
(Self::String(current), Self::Bool(new)) => *current = Some(new.to_string()),
|
|
||||||
(Self::String(current), Self::Int(new)) => *current = Some(new.to_string()),
|
|
||||||
(Self::Bool(current), Self::Bool(new)) => *current = new,
|
|
||||||
(Self::Int(current), Self::Int(new)) => *current = new,
|
|
||||||
|
|
||||||
_ => panic!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn str(&self) -> Option<&str> {
|
|
||||||
match self {
|
|
||||||
Self::String(opt) => opt.as_ref().map(|s| s.as_str()),
|
|
||||||
_ => panic!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn bool(&self) -> bool {
|
|
||||||
match self {
|
|
||||||
Self::Bool(bool) => *bool,
|
|
||||||
_ => panic!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn byte(&self) -> u8 {
|
|
||||||
match self {
|
|
||||||
Self::Int(b) => *b,
|
|
||||||
_ => panic!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<String> for AttributeValue {
|
|
||||||
fn from(value: String) -> Self {
|
|
||||||
Self::String(Some(value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Option<String>> for AttributeValue {
|
|
||||||
fn from(value: Option<String>) -> Self {
|
|
||||||
Self::String(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<bool> for AttributeValue {
|
|
||||||
fn from(value: bool) -> Self {
|
|
||||||
Self::Bool(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<u8> for AttributeValue {
|
|
||||||
fn from(value: u8) -> Self {
|
|
||||||
Self::Int(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct E1 {
|
pub struct E1 {
|
||||||
modes: HashMap<u32, String>,
|
modes: HashMap<u32, String>,
|
||||||
attributes: HashMap<DeviceAttributes, AttributeValue>,
|
attributes: HashMap<&'static str, AttributeValue>,
|
||||||
|
|
||||||
status: [&'static str; 5],
|
status: [&'static str; 5],
|
||||||
progress: [&'static str; 6],
|
progress: [&'static str; 6],
|
||||||
|
@ -205,39 +50,30 @@ impl E1 {
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let attributes = [
|
let attributes = [
|
||||||
(DeviceAttributes::Power, AttributeValue::Bool(false)),
|
("power", AttributeValue::Bool(false)),
|
||||||
(DeviceAttributes::Status, AttributeValue::String(None)),
|
("status", AttributeValue::Int(0)),
|
||||||
(DeviceAttributes::Mode, AttributeValue::Int(0)),
|
("mode", AttributeValue::Int(0)),
|
||||||
(DeviceAttributes::Additional, AttributeValue::Int(0)),
|
("additional", AttributeValue::Int(0)),
|
||||||
(DeviceAttributes::UV, AttributeValue::Bool(false)),
|
("uv", AttributeValue::Bool(false)),
|
||||||
(DeviceAttributes::Dry, AttributeValue::Bool(false)),
|
("dry", AttributeValue::Bool(false)),
|
||||||
(DeviceAttributes::DryStatus, AttributeValue::Bool(false)),
|
("dry_status", AttributeValue::Bool(false)),
|
||||||
(DeviceAttributes::Door, AttributeValue::Bool(false)),
|
("door", AttributeValue::Bool(false)),
|
||||||
(DeviceAttributes::RinseAid, AttributeValue::Bool(false)),
|
("rinse_aid", AttributeValue::Bool(false)),
|
||||||
(DeviceAttributes::Salt, AttributeValue::Bool(false)),
|
("salt", AttributeValue::Bool(false)),
|
||||||
(DeviceAttributes::ChildLock, AttributeValue::Bool(false)),
|
("child_lock", AttributeValue::Bool(false)),
|
||||||
(DeviceAttributes::Storage, AttributeValue::Bool(false)),
|
("storage", AttributeValue::Bool(false)),
|
||||||
(DeviceAttributes::StorageStatus, AttributeValue::Bool(false)),
|
("storage_progress", AttributeValue::Bool(false)),
|
||||||
(
|
("time_remaining", AttributeValue::String(None)),
|
||||||
DeviceAttributes::TimeRemaining,
|
("progress", AttributeValue::String(None)),
|
||||||
AttributeValue::String(None),
|
("storage_remaining", AttributeValue::String(None)),
|
||||||
),
|
("temperature", AttributeValue::String(None)),
|
||||||
(DeviceAttributes::Progress, AttributeValue::String(None)),
|
("humidity", AttributeValue::String(None)),
|
||||||
(
|
("waterswitch", AttributeValue::Bool(false)),
|
||||||
DeviceAttributes::StorageRemaining,
|
("water_lack", AttributeValue::Bool(false)),
|
||||||
AttributeValue::String(None),
|
("error_code", AttributeValue::String(None)),
|
||||||
),
|
("softwater", AttributeValue::Int(0)),
|
||||||
(DeviceAttributes::Temperature, AttributeValue::String(None)),
|
("wrong_operation", AttributeValue::String(None)),
|
||||||
(DeviceAttributes::Humidity, AttributeValue::String(None)),
|
("bright", AttributeValue::Int(0)),
|
||||||
(DeviceAttributes::Waterswitch, AttributeValue::Bool(false)),
|
|
||||||
(DeviceAttributes::WaterLack, AttributeValue::Bool(false)),
|
|
||||||
(DeviceAttributes::ErrorCode, AttributeValue::String(None)),
|
|
||||||
(DeviceAttributes::Softwater, AttributeValue::Int(0)),
|
|
||||||
(
|
|
||||||
DeviceAttributes::WrongOperation,
|
|
||||||
AttributeValue::String(None),
|
|
||||||
),
|
|
||||||
(DeviceAttributes::Bright, AttributeValue::Int(0)),
|
|
||||||
]
|
]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -261,31 +97,23 @@ impl DeviceBackend for E1 {
|
||||||
CommandQuery::new(self.device_protocol_version).request()
|
CommandQuery::new(self.device_protocol_version).request()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_message(&self, msg: &[u8]) -> Result<Vec<u8>> {
|
fn process_message(&mut self, msg: &[u8]) {
|
||||||
let cmd = CommandE1Response::new(msg);
|
CommandE1Response::new(msg).update_attributes(&mut self.attributes);
|
||||||
|
|
||||||
todo!()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_attribute(&self, attribute: &str, value: &str) -> () {
|
fn set_attribute(&self, attribute: &str, value: &str) -> () {
|
||||||
match DeviceAttributes::from_str(attribute) {
|
match attribute {
|
||||||
DeviceAttributes::Power => (),
|
"power" => (),
|
||||||
DeviceAttributes::ChildLock => (),
|
"child_lock" => (),
|
||||||
DeviceAttributes::Storage => (),
|
"storage" => (),
|
||||||
|
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn attributes(&self) -> &HashMap<&'static str, AttributeValue> {
|
||||||
|
&self.attributes
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! concat {
|
|
||||||
($vec1:expr, $($vec2:expr,)+) => {{
|
|
||||||
$(
|
|
||||||
$vec1.extend(&$vec2);
|
|
||||||
)+
|
|
||||||
|
|
||||||
$vec1
|
|
||||||
}};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CommandE1Base {
|
pub struct CommandE1Base {
|
||||||
|
@ -367,7 +195,7 @@ impl CommandLock {
|
||||||
command: CommandE1Base::new(
|
command: CommandE1Base::new(
|
||||||
device_protocol_version,
|
device_protocol_version,
|
||||||
MessageType::Set,
|
MessageType::Set,
|
||||||
Body::from((concat!(vec![0x04], vec![0x00; 36],).as_slice(), 0x08)),
|
Body::from(([vec![0x04], vec![0x00; 36]].concat().as_slice(), 0x08)),
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
"Lock",
|
"Lock",
|
||||||
),
|
),
|
||||||
|
@ -406,7 +234,9 @@ impl CommandStorage {
|
||||||
device_protocol_version,
|
device_protocol_version,
|
||||||
MessageType::Set,
|
MessageType::Set,
|
||||||
Body::from((
|
Body::from((
|
||||||
concat!(vec![0x00; 4], vec![0xFF; 6], vec![0x00; 27],).as_slice(),
|
[vec![0x00; 4], vec![0xFF; 6], vec![0x00; 27]]
|
||||||
|
.concat()
|
||||||
|
.as_slice(),
|
||||||
0x81,
|
0x81,
|
||||||
)),
|
)),
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
|
@ -473,50 +303,35 @@ impl CommandE1Response {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_attributes(
|
pub fn update_attributes(&mut self, attributes: &mut HashMap<&'static str, AttributeValue>) {
|
||||||
&mut self,
|
|
||||||
attributes: &mut HashMap<DeviceAttributes, AttributeValue>,
|
|
||||||
) {
|
|
||||||
let message_type = self.command.header().message_type();
|
let message_type = self.command.header().message_type();
|
||||||
let body = self.command.body();
|
let body = self.command.body();
|
||||||
let body_type = self.command.body().body_type();
|
let body_type = self.command.body().body_type();
|
||||||
|
|
||||||
if (message_type == MessageType::Set && 0 <= body_type && body_type <= 7)
|
if (message_type == MessageType::Set && body_type <= 7)
|
||||||
|| ((message_type == MessageType::Query || message_type == MessageType::Notify1)
|
|| ((message_type == MessageType::Query || message_type == MessageType::Notify1)
|
||||||
&& body_type == 0)
|
&& body_type == 0)
|
||||||
{
|
{
|
||||||
attributes
|
attributes.get_mut("power").unwrap().set(body[1] > 0);
|
||||||
.get_mut(&DeviceAttributes::Power)
|
attributes.get_mut("status").unwrap().set(body[1]);
|
||||||
.unwrap()
|
attributes.get_mut("mode").unwrap().set(body[2]);
|
||||||
.set(body[1] > 0);
|
attributes.get_mut("additional").unwrap().set(body[3]);
|
||||||
attributes
|
|
||||||
.get_mut(&DeviceAttributes::Status)
|
|
||||||
.unwrap()
|
|
||||||
.set(body[1]);
|
|
||||||
attributes
|
|
||||||
.get_mut(&DeviceAttributes::Mode)
|
|
||||||
.unwrap()
|
|
||||||
.set(body[2]);
|
|
||||||
attributes
|
|
||||||
.get_mut(&DeviceAttributes::Additional)
|
|
||||||
.unwrap()
|
|
||||||
.set(body[3]);
|
|
||||||
|
|
||||||
// 0 - open, 1 - close
|
// 0 - open, 1 - close
|
||||||
attributes
|
attributes
|
||||||
.get_mut(&DeviceAttributes::Door)
|
.get_mut("door")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.set((body[5] & 0x01) == 0);
|
.set((body[5] & 0x01) == 0);
|
||||||
|
|
||||||
// 0 - enough, 1 - shortage
|
// 0 - enough, 1 - shortage
|
||||||
attributes
|
attributes
|
||||||
.get_mut(&DeviceAttributes::RinseAid)
|
.get_mut("rinse_aid")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.set((body[5] & 0x02) > 0);
|
.set((body[5] & 0x02) > 0);
|
||||||
|
|
||||||
// e - enough, 1 - shortage
|
// e - enough, 1 - shortage
|
||||||
attributes
|
attributes
|
||||||
.get_mut(&DeviceAttributes::Salt)
|
.get_mut("salt")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.set((body[5] & 0x04) > 0);
|
.set((body[5] & 0x04) > 0);
|
||||||
|
|
||||||
|
@ -524,58 +339,41 @@ impl CommandE1Response {
|
||||||
|
|
||||||
if start_pause {
|
if start_pause {
|
||||||
self.start = true;
|
self.start = true;
|
||||||
} else if attributes[&DeviceAttributes::Status].byte() == 2
|
} else if attributes["status"].byte() == 2 || attributes["status"].byte() == 3 {
|
||||||
|| attributes[&DeviceAttributes::Status].byte() == 3
|
|
||||||
{
|
|
||||||
self.start = false;
|
self.start = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes
|
attributes
|
||||||
.get_mut(&DeviceAttributes::ChildLock)
|
.get_mut("child_lock")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.set((body[5] & 0x10) > 0);
|
.set((body[5] & 0x10) > 0);
|
||||||
|
attributes.get_mut("uv").unwrap().set((body[4] & 0x02) > 0);
|
||||||
|
attributes.get_mut("dry").unwrap().set((body[4] & 0x10) > 0);
|
||||||
attributes
|
attributes
|
||||||
.get_mut(&DeviceAttributes::UV)
|
.get_mut("dry_status")
|
||||||
.unwrap()
|
|
||||||
.set((body[4] & 0x02) > 0);
|
|
||||||
attributes
|
|
||||||
.get_mut(&DeviceAttributes::Dry)
|
|
||||||
.unwrap()
|
|
||||||
.set((body[4] & 0x10) > 0);
|
|
||||||
attributes
|
|
||||||
.get_mut(&DeviceAttributes::DryStatus)
|
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.set((body[4] & 0x20) > 0);
|
.set((body[4] & 0x20) > 0);
|
||||||
attributes
|
attributes
|
||||||
.get_mut(&DeviceAttributes::Status)
|
.get_mut("storage")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.set((body[5] & 0x20) > 0);
|
.set((body[5] & 0x20) > 0);
|
||||||
attributes
|
attributes
|
||||||
.get_mut(&DeviceAttributes::StorageStatus)
|
.get_mut("storage_progress")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.set((body[5] & 0x40) > 0);
|
.set((body[5] & 0x40) > 0);
|
||||||
|
attributes.get_mut("time_remaining").unwrap().set(body[6]);
|
||||||
|
attributes.get_mut("progress").unwrap().set(body[9]);
|
||||||
attributes
|
attributes
|
||||||
.get_mut(&DeviceAttributes::TimeRemaining)
|
.get_mut("storage_remaining")
|
||||||
.unwrap()
|
|
||||||
.set(body[6]);
|
|
||||||
attributes
|
|
||||||
.get_mut(&DeviceAttributes::Progress)
|
|
||||||
.unwrap()
|
|
||||||
.set(body[9]);
|
|
||||||
attributes
|
|
||||||
.get_mut(&DeviceAttributes::StorageRemaining)
|
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.set(if body.len() > 18 {
|
.set(if body.len() > 18 {
|
||||||
body[11]
|
body[11]
|
||||||
} else {
|
} else {
|
||||||
false.into()
|
false.into()
|
||||||
});
|
});
|
||||||
|
attributes.get_mut("temperature").unwrap().set(body[11]);
|
||||||
attributes
|
attributes
|
||||||
.get_mut(&DeviceAttributes::Temperature)
|
.get_mut("humidity")
|
||||||
.unwrap()
|
|
||||||
.set(body[11]);
|
|
||||||
attributes
|
|
||||||
.get_mut(&DeviceAttributes::Humidity)
|
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.set(if body.len() > 33 {
|
.set(if body.len() > 33 {
|
||||||
body[33].into()
|
body[33].into()
|
||||||
|
@ -583,27 +381,18 @@ impl CommandE1Response {
|
||||||
AttributeValue::from(None)
|
AttributeValue::from(None)
|
||||||
});
|
});
|
||||||
attributes
|
attributes
|
||||||
.get_mut(&DeviceAttributes::Waterswitch)
|
.get_mut("waterswitch")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.set((body[4] & 0x04) > 0);
|
.set((body[4] & 0x04) > 0);
|
||||||
attributes
|
attributes
|
||||||
.get_mut(&DeviceAttributes::WaterLack)
|
.get_mut("water_lack")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.set((body[5] & 0x80) > 0);
|
.set((body[5] & 0x80) > 0);
|
||||||
|
attributes.get_mut("error_code").unwrap().set(body[10]);
|
||||||
|
attributes.get_mut("softwater").unwrap().set(body[13]);
|
||||||
|
attributes.get_mut("wrong_operation").unwrap().set(body[16]);
|
||||||
attributes
|
attributes
|
||||||
.get_mut(&DeviceAttributes::ErrorCode)
|
.get_mut("bright")
|
||||||
.unwrap()
|
|
||||||
.set(body[10]);
|
|
||||||
attributes
|
|
||||||
.get_mut(&DeviceAttributes::Softwater)
|
|
||||||
.unwrap()
|
|
||||||
.set(body[13]);
|
|
||||||
attributes
|
|
||||||
.get_mut(&DeviceAttributes::WrongOperation)
|
|
||||||
.unwrap()
|
|
||||||
.set(body[16]);
|
|
||||||
attributes
|
|
||||||
.get_mut(&DeviceAttributes::Bright)
|
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.set(if body.len() > 24 {
|
.set(if body.len() > 24 {
|
||||||
body[24].into()
|
body[24].into()
|
||||||
|
|
|
@ -1,11 +1,78 @@
|
||||||
use anyhow::Result;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::command::CommandRequest;
|
use crate::command::CommandRequest;
|
||||||
|
|
||||||
pub mod e1;
|
pub mod e1;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub enum AttributeValue {
|
||||||
|
String(Option<String>),
|
||||||
|
Bool(bool),
|
||||||
|
Int(u8),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AttributeValue {
|
||||||
|
pub fn set(&mut self, value: impl Into<Self>) {
|
||||||
|
match (self, value.into()) {
|
||||||
|
(Self::String(current), Self::String(new)) => *current = new,
|
||||||
|
(Self::String(current), Self::Bool(new)) => *current = Some(new.to_string()),
|
||||||
|
(Self::String(current), Self::Int(new)) => *current = Some(new.to_string()),
|
||||||
|
(Self::Bool(current), Self::Bool(new)) => *current = new,
|
||||||
|
(Self::Int(current), Self::Int(new)) => *current = new,
|
||||||
|
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn str(&self) -> Option<&str> {
|
||||||
|
match self {
|
||||||
|
Self::String(opt) => opt.as_ref().map(|s| s.as_str()),
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bool(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::Bool(bool) => *bool,
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn byte(&self) -> u8 {
|
||||||
|
match self {
|
||||||
|
Self::Int(b) => *b,
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for AttributeValue {
|
||||||
|
fn from(value: String) -> Self {
|
||||||
|
Self::String(Some(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Option<String>> for AttributeValue {
|
||||||
|
fn from(value: Option<String>) -> Self {
|
||||||
|
Self::String(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<bool> for AttributeValue {
|
||||||
|
fn from(value: bool) -> Self {
|
||||||
|
Self::Bool(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u8> for AttributeValue {
|
||||||
|
fn from(value: u8) -> Self {
|
||||||
|
Self::Int(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait DeviceBackend {
|
pub trait DeviceBackend {
|
||||||
fn build_query(&self) -> CommandRequest;
|
fn build_query(&self) -> CommandRequest;
|
||||||
fn process_message(&self, msg: &[u8]) -> Result<Vec<u8>>;
|
fn process_message(&mut self, msg: &[u8]);
|
||||||
|
fn attributes(&self) -> &HashMap<&'static str, AttributeValue>;
|
||||||
fn set_attribute(&self, attribute: &str, value: &str) -> ();
|
fn set_attribute(&self, attribute: &str, value: &str) -> ();
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,16 +62,7 @@ impl<S: RequestSerializer> PacketBuilder<S> {
|
||||||
packet[3] = 0x10;
|
packet[3] = 0x10;
|
||||||
packet[6] = 0x7b;
|
packet[6] = 0x7b;
|
||||||
} else {
|
} else {
|
||||||
let mut data = self.command.serialize();
|
packet.extend(Security::aes_encrypt(&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);
|
|
||||||
|
|
||||||
packet.extend(data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// packet length
|
// packet length
|
||||||
|
|
|
@ -3,7 +3,7 @@ use std::sync::atomic::{AtomicU16, Ordering::SeqCst};
|
||||||
use aes::{
|
use aes::{
|
||||||
cipher::{
|
cipher::{
|
||||||
block_padding::NoPadding, generic_array::GenericArray, BlockDecrypt, BlockDecryptMut,
|
block_padding::NoPadding, generic_array::GenericArray, BlockDecrypt, BlockDecryptMut,
|
||||||
BlockEncrypt, BlockEncryptMut, KeyInit, KeyIvInit,
|
BlockEncrypt, BlockEncryptMut, BlockSizeUser, KeyInit, KeyIvInit,
|
||||||
},
|
},
|
||||||
Aes128,
|
Aes128,
|
||||||
};
|
};
|
||||||
|
@ -40,7 +40,7 @@ impl Security {
|
||||||
md5::compute([data, Self::SALT].concat()).to_vec()
|
md5::compute([data, Self::SALT].concat()).to_vec()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn aes_encrypt(data: &mut [u8]) -> Vec<u8> {
|
pub fn aes_encrypt(data: &[u8]) -> Vec<u8> {
|
||||||
let array = GenericArray::from(Self::KEY);
|
let array = GenericArray::from(Self::KEY);
|
||||||
let cipher = Aes128::new(&array);
|
let cipher = Aes128::new(&array);
|
||||||
|
|
||||||
|
@ -74,27 +74,21 @@ impl Security {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn aes_cbc_encrypt(&self, raw: &[u8], key: &[u8; 32]) -> Vec<u8> {
|
pub fn aes_cbc_encrypt(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);
|
debug_assert_eq!(raw.len() % Aes256CbcEnc::block_size(), 0);
|
||||||
|
|
||||||
raw.chunks(key.len())
|
Aes256CbcEnc::new(key.into(), &Self::IV.into()).encrypt_padded_vec_mut::<NoPadding>(raw)
|
||||||
.map(|r| {
|
|
||||||
Aes256CbcEnc::new(key.into(), &Self::IV.into())
|
|
||||||
.encrypt_padded_vec_mut::<NoPadding>(r)
|
|
||||||
})
|
|
||||||
.flatten()
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn aes_cbc_decrypt(&self, raw: [u8; 32], key: &[u8; 32]) -> [u8; 32] {
|
pub fn aes_cbc_decrypt(raw: &[u8], key: &[u8; 32]) -> Vec<u8> {
|
||||||
type Aes256CbcDec = cbc::Decryptor<aes::Aes256>;
|
type Aes256CbcDec = cbc::Decryptor<aes::Aes256>;
|
||||||
|
|
||||||
|
debug_assert_eq!(raw.len() % Aes256CbcDec::block_size(), 0);
|
||||||
|
|
||||||
Aes256CbcDec::new(key.into(), &Self::IV.into())
|
Aes256CbcDec::new(key.into(), &Self::IV.into())
|
||||||
.decrypt_padded_vec_mut::<NoPadding>(&raw)
|
.decrypt_padded_vec_mut::<NoPadding>(raw)
|
||||||
.unwrap()
|
|
||||||
.try_into()
|
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,15 +97,9 @@ impl Security {
|
||||||
bail!("authentication failed! (code ERROR)");
|
bail!("authentication failed! (code ERROR)");
|
||||||
}
|
}
|
||||||
|
|
||||||
let payload: [u8; 32] = response[0..32]
|
let payload = &response[0..32];
|
||||||
.iter()
|
|
||||||
.map(|&b| b)
|
|
||||||
.collect::<Vec<u8>>()
|
|
||||||
.try_into()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let sign = &response[32..];
|
let sign = &response[32..];
|
||||||
let result = self.aes_cbc_decrypt(payload, &key);
|
let result = Self::aes_cbc_decrypt(payload, &key);
|
||||||
|
|
||||||
if Sha256::digest(&result).into_iter().collect::<Vec<u8>>() != sign {
|
if Sha256::digest(&result).into_iter().collect::<Vec<u8>>() != sign {
|
||||||
bail!("sign does not match");
|
bail!("sign does not match");
|
||||||
|
@ -168,7 +156,7 @@ impl Security {
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
data = self.aes_cbc_encrypt(&data, self.tcp_key.as_ref().unwrap());
|
data = Self::aes_cbc_encrypt(&data, self.tcp_key.as_ref().unwrap());
|
||||||
data.extend(sign);
|
data.extend(sign);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,14 +197,24 @@ impl Security {
|
||||||
let msgtype = header[5] & 0xf;
|
let msgtype = header[5] & 0xf;
|
||||||
data = data[6..].to_vec();
|
data = data[6..].to_vec();
|
||||||
|
|
||||||
|
let header_ref = b"\x83p\x00\x8e c";
|
||||||
|
|
||||||
|
assert_eq!(header_ref, header.as_slice());
|
||||||
|
|
||||||
if msgtype == MsgType::ENCRYPTED_RESPONSE as u8
|
if msgtype == MsgType::ENCRYPTED_RESPONSE as u8
|
||||||
|| msgtype == MsgType::ENCRYPTED_REQUEST as u8
|
|| msgtype == MsgType::ENCRYPTED_REQUEST as u8
|
||||||
{
|
{
|
||||||
let sign = data[(data.len() - 32)..].to_vec();
|
let sign = data[(data.len() - 32)..].to_vec();
|
||||||
|
// let sign_ref = b"\xf5\x1c\x93\x18\xa3\xb9HR.X\x9aFU\xb1\xc4N\xe8J\x1a\x04\x0f\xd4\x90\xc4\x132\xeby\xa6\x83\x06e";
|
||||||
|
|
||||||
|
// assert_eq!(sign, sign_ref);
|
||||||
|
|
||||||
data = data[..(data.len() - 32)].to_vec();
|
data = data[..(data.len() - 32)].to_vec();
|
||||||
data = self
|
let data_ref = b"\xb2\x174\xbb\xf4\xf1\x8fWg\xc4\x97h%\x06\x9aCIL\xf6tf\x86\x8b\x83\xac\xa9@\xb1r\xb3\xa0>>\xd0_#\x03\x93\xcdZ[\x17\xde\xd8\xaeT\x1a\xd0\x0f\xa5\x8fk\xebJF\x0b\xfd\xf8\\o_\x06\xe9\xdd\xda45\x0b\x90\xf1\x82*\x9f\xa4\x1a\xc3\xb3\xde]\x04\x9e\x9c\xafy\xcfq\x02\xa34\xa4\x15\x0b\xcb\t\x96V\xd3f\x14\xd6\xf5X]\xc3'\xbc\x1b\xc1\x10\x08\x9c\x8d";
|
||||||
.aes_cbc_decrypt(data.try_into().unwrap(), &self.tcp_key.unwrap())
|
|
||||||
.to_vec();
|
// assert_eq!(data, data_ref);
|
||||||
|
|
||||||
|
data = Self::aes_cbc_decrypt(&data, &self.tcp_key.unwrap()).to_vec();
|
||||||
|
|
||||||
let compare_sign: Vec<u8> = Sha256::digest(&[header.to_vec(), data.clone()].concat())
|
let compare_sign: Vec<u8> = Sha256::digest(&[header.to_vec(), data.clone()].concat())
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -254,21 +252,35 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn aes_cbc_decrypt() {
|
fn aes_cbc_decrypt() {
|
||||||
let payload: [u8; 32] =
|
let payload =
|
||||||
b",\xcbq_T\x81L\x96\xfa\xe7\xe4\xa7\xc5\xabU \r\xf5x\xd6\x08\x94_\\\xce\x8br\x1b\xa5\xbe\xc6\x1a"
|
b",\xcbq_T\x81L\x96\xfa\xe7\xe4\xa7\xc5\xabU \r\xf5x\xd6\x08\x94_\\\xce\x8br\x1b\xa5\xbe\xc6\x1a";
|
||||||
.iter()
|
|
||||||
.map(|&b| b)
|
|
||||||
.collect::<Vec<u8>>()
|
|
||||||
.try_into()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let key = b"*[R\x00\xc2\xc0ML\x81\x1d\x05P\xe1\xdc[1CT6\xb9[wM*\x88\xd7\xe4ma\xfd\x96i";
|
let key = b"*[R\x00\xc2\xc0ML\x81\x1d\x05P\xe1\xdc[1CT6\xb9[wM*\x88\xd7\xe4ma\xfd\x96i";
|
||||||
let plain = b"\x9b\xaa\xdf\xff\x07\x1a\xd2\xe4\xb7TY\xe2\xf9\x8c\xdf\xe7!+\xda\xe4\x86GY\xe6j\x94\xdb\xe7\xb9b\xda\xe6";
|
let plain = b"\x9b\xaa\xdf\xff\x07\x1a\xd2\xe4\xb7TY\xe2\xf9\x8c\xdf\xe7!+\xda\xe4\x86GY\xe6j\x94\xdb\xe7\xb9b\xda\xe6";
|
||||||
|
|
||||||
let security = Security::default();
|
let result = Security::aes_cbc_decrypt(payload, key);
|
||||||
|
|
||||||
let result = security.aes_cbc_decrypt(payload, key);
|
|
||||||
|
|
||||||
assert_eq!(&result, plain);
|
assert_eq!(&result, plain);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn xor() {
|
||||||
|
let lhs = b"\xf8\x9c\xd9\xb4{>X\x95Y\xff\xd9\x95\x98nY\x95\x89\x8eY\x95\xe9\x86Y\x95\xe1\xb6Y\x95\xd1\xb2Y\x95";
|
||||||
|
let rhs = b"P\xe7yG\xdccBm\xb3\x88<v\x16a4\x10\x04K\x02V\x7fR@\xa5\xba\xf7\x899\x1b.Zy";
|
||||||
|
|
||||||
|
let res = Security::xorstr(lhs, rhs);
|
||||||
|
|
||||||
|
assert_eq!(res, b"\xa8{\xa0\xf3\xa7]\x1a\xf8\xeaw\xe5\xe3\x8e\x0fm\x85\x8d\xc5[\xc3\x96\xd4\x190[A\xd0\xac\xca\x9c\x03\xec");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn aes_cbc_decrypt_padding() {
|
||||||
|
let data = b"~\xb3\xb2\xa3x\t\x95\\\xe5U\x08\xba\t\xafe\xd0M\x0f\xf2\xdb\xda!\xb9\xab\x08#\xcf\xb3\x1f\xb3K\xa5\x85\x97\x06\xb4s\xaa\x11\xde\xb7\xf4%<J~\x9d\xdd\x9c8\xa1+\xdd\xa2\xdb\xf0\xc5)\xb3Oa\xb0\xbd\xbf\xf8\xd0Ea~f*\xc7}\xa9\x89\xb8\x9b\x89\xd8\xaci\x11S}\xd4Z\x981z\xed\xbe\xcf\xc8V\xe8\xe9\x19.\x8d\xc9\xf6`\xb7\xc3\xf7\x98O$\x19\x0bUz";
|
||||||
|
let key = b"V\x103\xba\xa0W\x85\xaa\x0c\x01q\xb7\x94\t\x7f\xd4\xe7=L\x91\x02\x14\x8d`\xf7~\xc6\xfd\x99?\x14\xbc";
|
||||||
|
|
||||||
|
let res_ref = b"\x00\x00ZZ\x01\x11h\x00@\x00\xfe\x11\x00\x00\xe9C1\x13\x02\n\x17\x14\x02\x86\x02\x00\x00\x8b\x00\x00\x00\x00\x00\x00\x00\x00\x01\x80\x00\x00\x00\x00\xb9qa\xcbB\xe7\x16\x97 b\xc8\xcd\x03N\x10\xd0\x94\xd9S\x10\xf9\x88\xbdZOX,\n\xe7L'\x1d\xf2p\xa5\x15\xf6\x9d\x1d>\x84z\xfe\xda\xd6Gx\x1d\n\xf6F\xac\xe9\xcd\xc5\xb9\xb8\x98\xae\x1b\n\xab\xc9\xee\x9a\xb6\xd2\xc5=:";
|
||||||
|
let res = Security::aes_cbc_decrypt(data, key);
|
||||||
|
|
||||||
|
assert_eq!(res.len(), res_ref.len());
|
||||||
|
assert_eq!(res, res_ref);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue