|
|
|
@ -20,6 +20,8 @@
|
|
|
|
|
--------------------------------------------------------------------------------------------
|
|
|
|
|
Version yyyymmdd Action Description
|
|
|
|
|
--------------------------------------------------------------------------------------------
|
|
|
|
|
0.9.1.4 20201020 changed - use BearSSL for decryption, revert to old TELEPERIOD-cycle as default
|
|
|
|
|
-------
|
|
|
|
|
0.9.1.3 20200926 changed - Improve HA discovery, make key+MAC case insensitive
|
|
|
|
|
-------
|
|
|
|
|
0.9.1.3 20200916 changed - add ATC (custom FW for LYWSD03MMC), API adaption for NimBLE-Arduino 1.0.2
|
|
|
|
@ -47,7 +49,7 @@
|
|
|
|
|
#include <NimBLEDevice.h>
|
|
|
|
|
#include <vector>
|
|
|
|
|
#ifdef USE_MI_DECRYPTION
|
|
|
|
|
#include <mbedtls/ccm.h>
|
|
|
|
|
#include <t_bearssl.h>
|
|
|
|
|
#endif //USE_MI_DECRYPTION
|
|
|
|
|
|
|
|
|
|
void MI32scanEndedCB(NimBLEScanResults results);
|
|
|
|
@ -81,11 +83,12 @@ struct {
|
|
|
|
|
uint8_t sensor; // points to to the number 0...255
|
|
|
|
|
} state;
|
|
|
|
|
struct {
|
|
|
|
|
uint32_t allwaysAggregate:1;
|
|
|
|
|
uint32_t allwaysAggregate:1; // always show all known values of one sensor in brdigemode
|
|
|
|
|
uint32_t noSummary:1; // no sensor values at TELE-period
|
|
|
|
|
uint32_t minimalSummary:1; // DEPRECATED!!
|
|
|
|
|
uint32_t directBridgeMode:1; // send every received BLE-packet as a MQTT-message in real-time
|
|
|
|
|
uint32_t showRSSI:1;
|
|
|
|
|
uint32_t ignoreBogusBattery:1;
|
|
|
|
|
uint32_t noSummary:1;
|
|
|
|
|
uint32_t minimalSummary:1;
|
|
|
|
|
} option;
|
|
|
|
|
} MI32;
|
|
|
|
|
|
|
|
|
@ -465,77 +468,71 @@ void MI32KeyMACStringToBytes(char* _string,uint8_t _keyMAC[]) { //uppercase
|
|
|
|
|
* @brief Decrypts payload in place
|
|
|
|
|
*
|
|
|
|
|
* @param _buf - pointer to the buffer at position of PID
|
|
|
|
|
* @param _bufSize - buffersize (last position is last byte of TAG)
|
|
|
|
|
* @param _bufSize - buffersize (last position is two bytes behind last byte of TAG)
|
|
|
|
|
* @param _type - sensor type
|
|
|
|
|
* @return int - error code, 0 for success
|
|
|
|
|
*/
|
|
|
|
|
int MI32_decryptPacket(char *_buf, uint16_t _bufSize, uint32_t _type){
|
|
|
|
|
encPacket_t *packet = (encPacket_t*)_buf;
|
|
|
|
|
// AddLog_P2(LOG_LEVEL_DEBUG,PSTR("to decrypt: %02x %02x %02x %02x %02x %02x %02x %02x"),(uint8_t)_buf[0],(uint8_t)_buf[1],(uint8_t)_buf[2],(uint8_t)_buf[3],(uint8_t)_buf[4],(uint8_t)_buf[5],(uint8_t)_buf[6],(uint8_t)_buf[7]);
|
|
|
|
|
// AddLog_P2(LOG_LEVEL_DEBUG,PSTR(" : %02x %02x %02x %02x %02x %02x %02x %02x"),(uint8_t)_buf[8],(uint8_t)_buf[9],(uint8_t)_buf[10],(uint8_t)_buf[11],(uint8_t)_buf[12],(uint8_t)_buf[13],(uint8_t)_buf[14],(uint8_t)_buf[15]);
|
|
|
|
|
// AddLog_P2(LOG_LEVEL_DEBUG,PSTR(" : %02x %02x %02x %02x %02x %02x %02x %02x"),(uint8_t)_buf[16],(uint8_t)_buf[17],(uint8_t)_buf[18],(uint8_t)_buf[19],(uint8_t)_buf[20],(uint8_t)_buf[21],(uint8_t)_buf[22],(uint8_t)_buf[23]);
|
|
|
|
|
// AddLog_P2(LOG_LEVEL_DEBUG,PSTR("as packet: MAC: %02x %02x %02x %02x %02x %02x"), packet->MAC[0], packet->MAC[1], packet->MAC[2], packet->MAC[3], packet->MAC[4], packet->MAC[5]);
|
|
|
|
|
|
|
|
|
|
AddLog_P2(LOG_LEVEL_DEBUG,PSTR("Decrypt Size of Buffer: %u"), _bufSize);
|
|
|
|
|
uint8_t payload[8];
|
|
|
|
|
size_t data_len = _bufSize - 9 - 4 - 3 - 1 - 1 ; // _bufsize - header - tag - ext.counter - RSSI - spare(?)
|
|
|
|
|
int ret = 0;
|
|
|
|
|
unsigned char output[10] = {0};
|
|
|
|
|
uint8_t nonce[12];
|
|
|
|
|
uint8_t tag[4 ];
|
|
|
|
|
uint32_t tag;
|
|
|
|
|
const unsigned char authData[1] = {0x11};
|
|
|
|
|
|
|
|
|
|
// AddLog_P2(LOG_LEVEL_DEBUG,PSTR("Decrypt Size of Buffer: %u, payload length: %u"), _bufSize, data_len);
|
|
|
|
|
// AddLogBuffer(LOG_LEVEL_DEBUG,(uint8_t*)_buf, _bufSize);
|
|
|
|
|
|
|
|
|
|
// nonce: device MAC, device type, frame cnt, ext. cnt
|
|
|
|
|
for (uint32_t i = 0; i<6; i++){
|
|
|
|
|
nonce[i] = packet->MAC[i];
|
|
|
|
|
}
|
|
|
|
|
memcpy((uint8_t*)&nonce+6,(uint8_t*)&packet->PID,2);
|
|
|
|
|
nonce[8] = packet->frameCnt;
|
|
|
|
|
memcpy((uint8_t*)&nonce+9,(char*)&_buf[_bufSize-9],3);
|
|
|
|
|
memcpy((uint8_t*)&nonce+9,(uint8_t*)&_buf[_bufSize-9],3);
|
|
|
|
|
// AddLog_P2(LOG_LEVEL_DEBUG,PSTR("nonceCnt1 and 2: %02x %02x %02x"),nonce[9],nonce[10],nonce[11]);
|
|
|
|
|
memcpy((uint8_t*)&tag,(char*)&_buf[_bufSize-6],4);
|
|
|
|
|
memcpy((uint8_t*)&tag,(uint8_t*)&_buf[_bufSize-6],4);
|
|
|
|
|
// AddLog_P2(LOG_LEVEL_DEBUG,PSTR("tag: %02x %02x %02x %02x"),tag[0],tag[1],tag[2],tag[3]);
|
|
|
|
|
|
|
|
|
|
MI32_ReverseMAC(packet->MAC);
|
|
|
|
|
uint8_t _bindkey[16] = {0x0};
|
|
|
|
|
bool foundNoKey = true;
|
|
|
|
|
AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MI32: search key for MAC: %02x %02x %02x %02x %02x %02x"), packet->MAC[0], packet->MAC[1], packet->MAC[2], packet->MAC[3], packet->MAC[4], packet->MAC[5]);
|
|
|
|
|
for(uint32_t i=0; i<MIBLEbindKeys.size(); i++){
|
|
|
|
|
if(memcmp(packet->MAC,MIBLEbindKeys[i].MAC,sizeof(packet->MAC))==0){
|
|
|
|
|
AddLog_P2(LOG_LEVEL_DEBUG,PSTR("have key"));
|
|
|
|
|
memcpy(_bindkey,MIBLEbindKeys[i].key,sizeof(_bindkey));
|
|
|
|
|
}
|
|
|
|
|
else{
|
|
|
|
|
// AddLog_P2(LOG_LEVEL_DEBUG,PSTR("Mac in packet: %02x %02x %02x %02x %02x %02x"), packet->MAC[0], packet->MAC[1], packet->MAC[2], packet->MAC[3], packet->MAC[4], packet->MAC[5]);
|
|
|
|
|
// AddLog_P2(LOG_LEVEL_DEBUG,PSTR("Mac in vector: %02x %02x %02x %02x %02x %02x"), MIBLEbindKeys[i].MAC[0], MIBLEbindKeys[i].MAC[1], MIBLEbindKeys[i].MAC[2], MIBLEbindKeys[i].MAC[3], MIBLEbindKeys[i].MAC[4], MIBLEbindKeys[i].MAC[5]);
|
|
|
|
|
AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MI32: decryption Key found"));
|
|
|
|
|
foundNoKey = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(foundNoKey){
|
|
|
|
|
AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MI32: no Key found !!"));
|
|
|
|
|
return -2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// init
|
|
|
|
|
mbedtls_ccm_context ctx;
|
|
|
|
|
mbedtls_ccm_init(&ctx);
|
|
|
|
|
// set bind key
|
|
|
|
|
ret = mbedtls_ccm_setkey(&ctx,
|
|
|
|
|
MBEDTLS_CIPHER_ID_AES,
|
|
|
|
|
_bindkey,
|
|
|
|
|
16 * 8 //bits
|
|
|
|
|
);
|
|
|
|
|
AddLog_P2(LOG_LEVEL_DEBUG,PSTR("set key: %i, MAC: %02x %02x %02x %02x %02x %02x"),ret, packet->MAC[0], packet->MAC[1], packet->MAC[2], packet->MAC[3], packet->MAC[4], packet->MAC[5]);
|
|
|
|
|
br_aes_small_ctrcbc_keys keyCtx;
|
|
|
|
|
br_aes_small_ctrcbc_init(&keyCtx, _bindkey, sizeof(_bindkey));
|
|
|
|
|
|
|
|
|
|
/*int mbedtls_ccm_auth_decrypt( mbedtls_ccm_context *ctx, size_t length,
|
|
|
|
|
const unsigned char *iv, size_t iv_len,
|
|
|
|
|
const unsigned char *add, size_t add_len,
|
|
|
|
|
const unsigned char *input, unsigned char *output,
|
|
|
|
|
const unsigned char *tag, size_t tag_len )
|
|
|
|
|
*/
|
|
|
|
|
ret = mbedtls_ccm_auth_decrypt(&ctx,_bufSize-18,
|
|
|
|
|
(const unsigned char*)&nonce, sizeof(nonce),
|
|
|
|
|
authData, sizeof(authData),
|
|
|
|
|
(const unsigned char*)packet->payload, output,
|
|
|
|
|
tag,sizeof(tag));
|
|
|
|
|
br_ccm_context ctx;
|
|
|
|
|
br_ccm_init(&ctx, &keyCtx.vtable);
|
|
|
|
|
br_ccm_reset(&ctx, nonce, sizeof(nonce), sizeof(authData), data_len, sizeof(tag));
|
|
|
|
|
br_ccm_aad_inject(&ctx, authData, sizeof(authData));
|
|
|
|
|
br_ccm_flip(&ctx);
|
|
|
|
|
|
|
|
|
|
AddLog_P2(LOG_LEVEL_DEBUG,PSTR("Decrypted %i: %02x %02x %02x %02x %02x %02x %02x"), ret, output[0],output[1],output[2],output[3],output[4],output[5],output[6]);
|
|
|
|
|
// put decrypted data in place
|
|
|
|
|
memcpy((uint8_t*)(packet->payload)+1,output,_bufSize-18);
|
|
|
|
|
// clean up
|
|
|
|
|
mbedtls_ccm_free(&ctx);
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
memcpy(payload,packet->payload,data_len); //we want to be sure about 4-byte alignement
|
|
|
|
|
br_ccm_run(&ctx, 0, payload, data_len);
|
|
|
|
|
memcpy((uint8_t*)packet->payload+1,payload,data_len); //back to the packet
|
|
|
|
|
|
|
|
|
|
// br_ccm_get_tag(&ctx, &checkTag);
|
|
|
|
|
ret = br_ccm_check_tag(&ctx, &tag);
|
|
|
|
|
// AddLog_P2(LOG_LEVEL_DEBUG,PSTR("packetTag: %08x"),tag);
|
|
|
|
|
// AddLog_P2(LOG_LEVEL_DEBUG,PSTR("computedTag: %08x"),checkTag);
|
|
|
|
|
|
|
|
|
|
AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MI32: Err:%i, Decrypted : %02x %02x %02x %02x %02x "), ret, packet->payload[1],packet->payload[2],packet->payload[3],packet->payload[4],packet->payload[5]);
|
|
|
|
|
return ret-1;
|
|
|
|
|
}
|
|
|
|
|
#endif // USE_MI_DECRYPTION
|
|
|
|
|
|
|
|
|
@ -651,6 +648,9 @@ void MI32triggerTele(void){
|
|
|
|
|
\*********************************************************************************************/
|
|
|
|
|
|
|
|
|
|
void MI32Init(void) {
|
|
|
|
|
|
|
|
|
|
MIBLEsensors.reserve(10);
|
|
|
|
|
MIBLEbindKeys.reserve(10);
|
|
|
|
|
MI32.mode.init = false;
|
|
|
|
|
if (!MI32.mode.init) {
|
|
|
|
|
NimBLEDevice::init("");
|
|
|
|
@ -661,10 +661,11 @@ void MI32Init(void) {
|
|
|
|
|
|
|
|
|
|
//test section for options
|
|
|
|
|
MI32.option.allwaysAggregate = 1;
|
|
|
|
|
MI32.option.noSummary = 0;
|
|
|
|
|
MI32.option.minimalSummary = 0;
|
|
|
|
|
MI32.option.directBridgeMode = 0;
|
|
|
|
|
MI32.option.showRSSI = 1;
|
|
|
|
|
MI32.option.ignoreBogusBattery = 1; // from advertisements
|
|
|
|
|
MI32.option.noSummary = 0;
|
|
|
|
|
MI32.option.minimalSummary = 1;
|
|
|
|
|
|
|
|
|
|
MI32StartScanTask(); // Let's get started !!
|
|
|
|
|
}
|
|
|
|
@ -1125,10 +1126,12 @@ void MI32parseMiBeacon(char * _buf, uint32_t _slot, uint16_t _bufSize){
|
|
|
|
|
|
|
|
|
|
MIBLEsensors[_slot].lastCnt = _beacon.counter;
|
|
|
|
|
#ifdef USE_MI_DECRYPTION
|
|
|
|
|
int decryptRet = 0;
|
|
|
|
|
switch(MIBLEsensors[_slot].type){
|
|
|
|
|
case LYWSD03MMC: case MHOC401:
|
|
|
|
|
if (_beacon.frame == 0x5858){
|
|
|
|
|
int decryptRet = MI32_decryptPacket((char*)&_beacon.productID,_bufSize, LYWSD03MMC); //start with PID
|
|
|
|
|
decryptRet = MI32_decryptPacket((char*)&_beacon.productID,_bufSize, LYWSD03MMC); //start with PID
|
|
|
|
|
// AddLogBuffer(LOG_LEVEL_DEBUG,(uint8_t*)&_beacon.productID,_bufSize);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case MJYD2S:
|
|
|
|
@ -1138,9 +1141,10 @@ void MI32parseMiBeacon(char * _buf, uint32_t _slot, uint16_t _bufSize){
|
|
|
|
|
memcpy((uint8_t*)&_beacon.MAC,MIBLEsensors[_slot].MAC,6); // now insert the real MAC from our internal vector
|
|
|
|
|
_bufSize+=6; // the packet has grown
|
|
|
|
|
MI32_ReverseMAC(_beacon.MAC); // payload MAC is always reversed
|
|
|
|
|
AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MJYD2S: special packet"));
|
|
|
|
|
}
|
|
|
|
|
if (_beacon.frame != 0x5910){
|
|
|
|
|
int decryptRet = MI32_decryptPacket((char*)&_beacon.productID,_bufSize,MJYD2S); //start with PID
|
|
|
|
|
decryptRet = MI32_decryptPacket((char*)&_beacon.productID,_bufSize,MJYD2S); //start with PID
|
|
|
|
|
}
|
|
|
|
|
else{
|
|
|
|
|
// This seems to be some kind of wake-up packet only, as it shows up before all kinds of messages, not only motion
|
|
|
|
@ -1156,22 +1160,22 @@ void MI32parseMiBeacon(char * _buf, uint32_t _slot, uint16_t _bufSize){
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
#endif //USE_MI_DECRYPTION
|
|
|
|
|
|
|
|
|
|
if (MIBLEsensors[_slot].type==NLIGHT){
|
|
|
|
|
AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MiBeacon type:%02x: %02x %02x %02x %02x %02x %02x %02x %02x"),_beacon.type, (uint8_t)_buf[0],(uint8_t)_buf[1],(uint8_t)_buf[2],(uint8_t)_buf[3],(uint8_t)_buf[4],(uint8_t)_buf[5],(uint8_t)_buf[6],(uint8_t)_buf[7]);
|
|
|
|
|
AddLog_P2(LOG_LEVEL_DEBUG,PSTR(" type:%02x: %02x %02x %02x %02x %02x %02x %02x %02x"),_beacon.type, (uint8_t)_buf[8],(uint8_t)_buf[9],(uint8_t)_buf[10],(uint8_t)_buf[11],(uint8_t)_buf[12],(uint8_t)_buf[13],(uint8_t)_buf[14],(uint8_t)_buf[15]);
|
|
|
|
|
if(decryptRet!=0){
|
|
|
|
|
AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MI32: decryption failed with error: %d"),decryptRet);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
#endif //USE_MI_DECRYPTION
|
|
|
|
|
|
|
|
|
|
if(MIBLEsensors[_slot].type==6){
|
|
|
|
|
DEBUG_SENSOR_LOG(PSTR("CGD1 no support for MiBeacon, type %u"),MIBLEsensors[_slot].type);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s at slot %u"), kMI32DeviceType[MIBLEsensors[_slot].type-1],_slot);
|
|
|
|
|
AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s at slot %u with payload type: %02x"), kMI32DeviceType[MIBLEsensors[_slot].type-1],_slot,_beacon.type);
|
|
|
|
|
switch(_beacon.type){
|
|
|
|
|
case 0x01:
|
|
|
|
|
MIBLEsensors[_slot].Btn=_beacon.Btn.num + (_beacon.Btn.longPress/2)*6;
|
|
|
|
|
MIBLEsensors[_slot].eventType.Btn = 1;
|
|
|
|
|
MI32.mode.shallTriggerTele = 1;
|
|
|
|
|
// AddLog_P2(LOG_LEVEL_DEBUG,PSTR("Mode 1: U16: %u Button"), MIBLEsensors[_slot].Btn );
|
|
|
|
|
break;
|
|
|
|
|
case 0x04:
|
|
|
|
@ -1248,11 +1252,13 @@ if (MIBLEsensors[_slot].type==NLIGHT){
|
|
|
|
|
MIBLEsensors[_slot].lux = _beacon.lux;
|
|
|
|
|
MIBLEsensors[_slot].eventType.lux = 1;
|
|
|
|
|
MIBLEsensors[_slot].NMT = 0;
|
|
|
|
|
MI32.mode.shallTriggerTele = 1;
|
|
|
|
|
// AddLog_P2(LOG_LEVEL_DEBUG,PSTR("PIR: primary"),MIBLEsensors[_slot].lux );
|
|
|
|
|
break;
|
|
|
|
|
case 0x17:
|
|
|
|
|
MIBLEsensors[_slot].NMT = _beacon.NMT;
|
|
|
|
|
MIBLEsensors[_slot].eventType.NMT = 1;
|
|
|
|
|
MI32.mode.shallTriggerTele = 1;
|
|
|
|
|
// AddLog_P2(LOG_LEVEL_DEBUG,PSTR("Mode 17: NMT: %u seconds"), _beacon.NMT);
|
|
|
|
|
break;
|
|
|
|
|
#endif //USE_MI_DECRYPTION
|
|
|
|
@ -1262,13 +1268,17 @@ if (MIBLEsensors[_slot].type==NLIGHT){
|
|
|
|
|
MIBLEsensors[_slot].events++;
|
|
|
|
|
MIBLEsensors[_slot].NMT = 0;
|
|
|
|
|
MIBLEsensors[_slot].lastTime = millis();
|
|
|
|
|
MI32.mode.shallTriggerTele = 1;
|
|
|
|
|
// AddLog_P2(LOG_LEVEL_DEBUG,PSTR("PIR: primary"),MIBLEsensors[_slot].lux );
|
|
|
|
|
}
|
|
|
|
|
else{
|
|
|
|
|
AddLogBuffer(LOG_LEVEL_DEBUG,(uint8_t*)_buf,_bufSize);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if(MIBLEsensors[_slot].eventType.raw == 0) return;
|
|
|
|
|
MIBLEsensors[_slot].shallSendMQTT = 1;
|
|
|
|
|
MI32.mode.shallTriggerTele = 1;
|
|
|
|
|
if(MI32.option.directBridgeMode) MI32.mode.shallTriggerTele = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MI32ParseATCPacket(char * _buf, uint32_t length, uint8_t addr[6], int rssi){
|
|
|
|
@ -1286,7 +1296,7 @@ void MI32ParseATCPacket(char * _buf, uint32_t length, uint8_t addr[6], int rssi)
|
|
|
|
|
MIBLEsensors[_slot].eventType.bat = 1;
|
|
|
|
|
|
|
|
|
|
MIBLEsensors[_slot].shallSendMQTT = 1;
|
|
|
|
|
MI32.mode.shallTriggerTele = 1;
|
|
|
|
|
if(MI32.option.directBridgeMode) MI32.mode.shallTriggerTele = 1;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1328,7 +1338,7 @@ void MI32parseCGD1Packet(char * _buf, uint32_t length, uint8_t addr[6], int rssi
|
|
|
|
|
}
|
|
|
|
|
if(MIBLEsensors[_slot].eventType.raw == 0) return;
|
|
|
|
|
MIBLEsensors[_slot].shallSendMQTT = 1;
|
|
|
|
|
MI32.mode.shallTriggerTele = 1;
|
|
|
|
|
if(MI32.option.directBridgeMode) MI32.mode.shallTriggerTele = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MI32ParseResponse(char *buf, uint16_t bufsize, uint8_t addr[6], int rssi) {
|
|
|
|
|