diff --git a/tasmota/xsns_62_MI_ESP32.ino b/tasmota/xsns_62_MI_ESP32.ino index 941f52950..eec8a79a8 100644 --- a/tasmota/xsns_62_MI_ESP32.ino +++ b/tasmota/xsns_62_MI_ESP32.ino @@ -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 #include #ifdef USE_MI_DECRYPTION -#include +#include #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; iMAC,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: @@ -1247,12 +1251,14 @@ if (MIBLEsensors[_slot].type==NLIGHT){ MIBLEsensors[_slot].events++; MIBLEsensors[_slot].lux = _beacon.lux; MIBLEsensors[_slot].eventType.lux = 1; - MIBLEsensors[_slot].NMT = 0; + 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) {