diff --git a/tasmota/xsns_61_MI_NRF24.ino b/tasmota/xsns_61_MI_NRF24.ino index 685c643ef..c1aa1f4b4 100644 --- a/tasmota/xsns_61_MI_NRF24.ino +++ b/tasmota/xsns_61_MI_NRF24.ino @@ -21,6 +21,8 @@ Version yyyymmdd Action Description -------------------------------------------------------------------------------------------- + 0.9.7.0 20200624 integrate - use BEARSSL-lib for decryption as default, make decryption optional + --- 0.9.6.1 20200622 integrate - use BEARSSL-lib for decryption as default, make decryption optional --- 0.9.6.0 20200618 integrate - add decryption for LYWSD03 @@ -56,7 +58,6 @@ #endif #define USE_MI_DECRYPTION -// #define USE_MBEDTLS /*********************************************************************************************\ * MINRF * BLE-Sniffer/Bridge for MIJIA/XIAOMI Temperatur/Humidity-Sensor, Mi Flora, LYWSD02, GCx @@ -68,12 +69,7 @@ #include #ifdef USE_MI_DECRYPTION -#ifdef USE_MBEDTLS -#include -#else #include -#include -#endif // USE_MBEDTLS #endif //USE_MI_DECRYPTION #define FLORA 1 @@ -82,12 +78,16 @@ #define LYWSD03 4 #define CGG1 5 #define CGD1 6 +#define NLIGHT 7 +#define MJYD2S 8 + +#define MI_TYPES 8 //count this manually #define D_CMND_NRF "NRF" const char S_JSON_NRF_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_NRF "%s\":%d}"; const char S_JSON_NRF_COMMAND[] PROGMEM = "{\"" D_CMND_NRF "%s\":\"%s\"}"; -const char kNRF_Commands[] PROGMEM = "Ignore|Page|Scan|Beacon|Chan" +const char kNRF_Commands[] PROGMEM = "Ignore|Page|Scan|Beacon|Chan|Nlight" #ifdef USE_MI_DECRYPTION "|Key" #endif //USE_MI_DECRYPTION @@ -98,18 +98,21 @@ enum NRF_Commands { // commands useable in console or rules CMND_NRF_PAGE, // sensor entries per web page, which will be shown alternated CMND_NRF_SCAN, // simplified passive BLE adv scan CMND_NRF_BEACON, // even more simplified Beacon, reports time since last sighting - CMND_NRF_CHAN // ignore channel 0-2 (translates to 37-39) + CMND_NRF_CHAN, // ignore channel 0-2 (translates to 37-39) + CMND_NRF_NLIGHT // add Philips night light via MAC #ifdef USE_MI_DECRYPTION , CMND_NRF_KEY // add bind_key to a MAC for payload decryption #endif //USE_MI_DECRYPTION }; -const uint16_t kMINRFSlaveID[6]={ 0x0098, // Flora +const uint16_t kMINRFSlaveID[8]={ 0x0098, // Flora 0x01aa, // MJ_HT_V1 0x045b, // LYWSD02 0x055b, // LYWSD03 0x0347, // CGG1 - 0x0576 // CGD1 + 0x0576, // CGD1 + 0x03dd, // NLIGHT + 0x07f6 // MJYD2S }; const char kMINRFSlaveType1[] PROGMEM = "Flora"; @@ -118,7 +121,9 @@ const char kMINRFSlaveType3[] PROGMEM = "LYWSD02"; const char kMINRFSlaveType4[] PROGMEM = "LYWSD03"; const char kMINRFSlaveType5[] PROGMEM = "CGG1"; const char kMINRFSlaveType6[] PROGMEM = "CGD1"; -const char * kMINRFSlaveType[] PROGMEM = {kMINRFSlaveType1,kMINRFSlaveType2,kMINRFSlaveType3,kMINRFSlaveType4,kMINRFSlaveType5,kMINRFSlaveType6}; +const char kMINRFSlaveType7[] PROGMEM = "NLIGHT"; +const char kMINRFSlaveType8[] PROGMEM = "MJYD2S"; +const char * kMINRFSlaveType[] PROGMEM = {kMINRFSlaveType1,kMINRFSlaveType2,kMINRFSlaveType3,kMINRFSlaveType4,kMINRFSlaveType5,kMINRFSlaveType6,kMINRFSlaveType7,kMINRFSlaveType8}; // PDU's or different channels 37-39 const uint32_t kMINRFFloPDU[3] = {0x3eaa857d,0xef3b8730,0x71da7b46}; @@ -128,6 +133,7 @@ const uint32_t kMINRFL3PDU[3] = {0x4760dd78,0xdbcc1ccd,0x33049deb}; //encrypted // const uint32_t kMINRFL3PDU[3] = {0x4760cb78,0xdbcc0acd,0x33048beb}; //unencrypted - 30 58 const uint32_t kMINRFCGGPDU[3] = {0x4760cd6e,0xdbcc0cdb,0x33048dfd}; const uint32_t kMINRFCGDPDU[3] = {0x5da0d752,0xc10c16e7,0x29c497c1}; +// const uint32_t kMINRFNLIPDU[3] = {0x4760C56E,0xDBCC04DB,0x0330485FD}; //NLIGHT // start-LSFR for different channels 37-39 const uint8_t kMINRFlsfrList_A[3] = {0x4b,0x17,0x23}; // Flora, LYWSD02 @@ -212,13 +218,14 @@ struct { const uint8_t frequency[3] = { 2,26,80}; // real frequency (2400+x MHz) uint16_t timer; + uint16_t ignore = 0; //bitfield: 2^sensor type uint8_t currentChan=0; - uint8_t ignore = 0; //bitfield: 2^sensor type uint8_t channelIgnore = 0; //bitfield: 2^channel (0=37,1=38,2=39) uint8_t confirmedSensors = 0; uint8_t packetMode; // 0 - normal BLE-advertisements, 1 - 6 "special" sensor packets uint8_t perPage = 4; uint8_t firstUsedPacketMode = 1; + uint8_t activeNlight = 0; FIFO_t buffer; @@ -256,6 +263,17 @@ struct mi_sensor_t{ }; }; +struct mi_nlight_t{ + uint8_t MAC[6]; + uint32_t PDU[3]; + uint8_t type; // NLIGHT=7 + struct { + uint16_t events; //"alarms" since boot + uint8_t lastCnt; //device generated counter of the packet + }; +}; + + struct scan_entry_t { uint8_t mac[6]; uint16_t cid; @@ -269,6 +287,7 @@ std::vector MINRFscanResult; #ifdef USE_MI_DECRYPTION std::vector MIBLEbindKeys; #endif //USE_MI_DECRYPTION +std::vector MIBLEnlights; static union{ scan_entry_t MINRFdummyEntry; @@ -370,6 +389,9 @@ bool MINRFreceivePacket(void) case 6: MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), kMINRFlsfrList_B[MINRF.currentChan]); // "CGD1" mode break; + case 7: + MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), MINRF.channel[MINRF.currentChan] | 0x40); // "NLIGHT" mode + break; } // DEBUG_SENSOR_LOG(PSTR("MINRF: LSFR:%x"),_lsfr); // if (_lsfr>254) _lsfr=0; @@ -598,24 +620,29 @@ void MINRFbeaconCounter(void){ * @brief compute "PDU" from MAC for each possible channel and store it globally * */ -void MINRFcomputeBeaconPDU(void){ +void MINRFcomputeBeaconPDU(uint8_t (&_mac)[6], uint32_t (&PDU)[3]){ + uint32_t _PDU[3]; for (uint32_t i = 0; i<3; i++){ bleAdvPacket_t packet; - memcpy((uint8_t *)&packet.mac, (uint8_t *)&MINRF.beacon.mac, sizeof(packet.mac)); + memcpy((uint8_t *)&packet.mac, (uint8_t *)&_mac, sizeof(packet.mac)); MINRFreverseMAC(packet.mac); MINRFwhiten((uint8_t *)&packet, sizeof(packet), MINRF.channel[i] | 0x40); MINRFswapbuf((uint8_t*)&packet,sizeof(packet)); uint32_t pdu = packet.mac[0]<<24 | packet.mac[1]<<16 | packet.mac[2]<<8 | packet.mac[3]; - MINRF.beacon.PDU[i] = pdu; + _PDU[i] = pdu; } + memcpy(PDU,_PDU,sizeof(_PDU)); } + #ifdef USE_MI_DECRYPTION int MINRFdecryptPacket(char *_buf){ 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 "),(uint8_t)_buf[16],(uint8_t)_buf[17],(uint8_t)_buf[18],(uint8_t)_buf[19],(uint8_t)_buf[20]); int ret = 0; - unsigned char output[10] = {0}; + unsigned char output[16] = {0}; uint8_t nonce[12]; const unsigned char authData[1] = {0x11}; @@ -640,33 +667,6 @@ int MINRFdecryptPacket(char *_buf){ // } } - #ifdef USE_MBEDTLS - // 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 - ); - - ret = mbedtls_ccm_auth_decrypt(&ctx,5, - (const unsigned char*)&nonce, sizeof(nonce), - authData, sizeof(authData), - packet->payload.cipher, output, - packet->payload.tag,sizeof(packet->payload.tag)); - - AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MBEDTLS: Err:%i, Decrypted : %02x %02x %02x %02x %02x "), ret, output[0],output[1],output[2],output[3],output[4]); - // put decrypted data in place - memcpy((uint8_t*)(packet->payload.cipher)+1,output,sizeof(packet->payload.cipher)); - // clean up - mbedtls_ccm_free(&ctx); - return ret; - - #else // BearSSL - memcpy(output,packet->payload.cipher, sizeof(packet->payload.cipher)); br_aes_small_ctrcbc_keys keyCtx; @@ -674,17 +674,15 @@ int MINRFdecryptPacket(char *_buf){ br_ccm_context ctx; br_ccm_init(&ctx, &keyCtx.vtable); - br_ccm_reset(&ctx, nonce, sizeof(nonce), sizeof(packet->payload.cipher), sizeof(authData), sizeof(packet->payload.tag)); + br_ccm_reset(&ctx, nonce, sizeof(nonce), sizeof(authData),sizeof(packet->payload.cipher),sizeof(packet->payload.tag)); br_ccm_aad_inject(&ctx, authData, sizeof(authData)); br_ccm_flip(&ctx); - br_ccm_run(&ctx, 0, output, sizeof(packet->payload.cipher)); + br_ccm_run(&ctx, 0, output, sizeof(packet->payload.cipher)); ret = br_ccm_check_tag(&ctx, packet->payload.tag); AddLog_P2(LOG_LEVEL_DEBUG,PSTR("BEARSSL: Err:%i, Decrypted : %02x %02x %02x %02x %02x "), ret, output[0],output[1],output[2],output[3],output[4]); memcpy((uint8_t*)(packet->payload.cipher)+1,output,sizeof(packet->payload.cipher)); - return ret; - #endif //USE_MBEDTLS - + return (ret-1); } #endif //USE_MI_DECRYPTION @@ -771,11 +769,11 @@ void MINRFMACStringToBytes(char* _string, uint8_t _mac[]) { //uppercase * */ void MINRFcomputefirstUsedPacketMode(void){ - for (uint32_t i = 0; iCGD1) MINRF.firstUsedPacketMode=0; + if(MINRF.firstUsedPacketMode>MI_TYPES) MINRF.firstUsedPacketMode=0; break; } } @@ -812,6 +810,11 @@ void MINRFchangePacketModeTo(uint8_t _mode) { case 6: // special CGD1 packet NRF24radio.openReadingPipe(0,kMINRFCGDPDU[_nextchannel]); // cd fd 08 0c -> CGD1 break; + case 7: // MAC based NLIGHT packet + if (MIBLEnlights.size()==0) break; + NRF24radio.openReadingPipe(0,MIBLEnlights[MINRF.activeNlight].PDU[_nextchannel]); // computed from MAC -> NLIGHT + MINRF.activeNlight++; + break; } // DEBUG_SENSOR_LOG(PSTR("MINRF: Change Mode to %u"),_mode); MINRF.packetMode = _mode; @@ -1031,6 +1034,44 @@ void MINRFhandleCGD1Packet(void){ // no MiBeacon } } +void MINRFhandleNlightPacket(void){ // no MiBeacon + uint32_t offset = 6; + uint8_t _buf[32+offset]; + MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), MINRF.channel[MINRF.currentChan] | 0x40); + MINRFswapbuf((uint8_t*)&MINRF.buffer,sizeof(MINRF.buffer)); + memcpy((uint8_t*)&_buf+offset,MINRF.buffer.raw,32); + MINRFswapbuf((uint8_t*)&_buf,sizeof(_buf)); + MINRFwhiten((uint8_t *)&_buf, sizeof(_buf), MINRF.channel[MINRF.currentChan] | 0x40); + if (offset == 6) MINRFreverseMAC((uint8_t*)&_buf[2]); + // AddLog_P2(LOG_LEVEL_INFO,PSTR("MINRF: NLIGHT: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x"),_buf[0],_buf[1],_buf[2],_buf[3],_buf[4],_buf[5],_buf[6],_buf[7],_buf[8],_buf[9],_buf[10],_buf[11],_buf[12],_buf[13],_buf[14],_buf[15],_buf[16],_buf[17],_buf[18]); + uint32_t _frame_PID = _buf[15]<<24 | _buf[16]<<16 | _buf[17]<<8 | _buf[18]; + if(_frame_PID!=0x4030dd03) return; + AddLog_P2(LOG_LEVEL_INFO,PSTR("MINRF: NLIGHT:%x"),_frame_PID); + uint32_t _idx = MINRF.activeNlight-1; + if(_buf[19]!=MIBLEnlights[_idx].lastCnt){ + MIBLEnlights[_idx].lastCnt = _buf[19]; + MIBLEnlights[_idx].events++; + AddLog_P2(LOG_LEVEL_INFO,PSTR("MINRF: NLIGHT %u: events: %u, Cnt:%u"), _idx,MIBLEnlights[_idx].events, MIBLEnlights[_idx].lastCnt); + } +} + +void MINRFaddNlight(uint8_t _mac[]){ // no MiBeacon + for(uint32_t i=0; iCGD1) ? MINRF.firstUsedPacketMode : MINRF.packetMode+1; - for (uint32_t i = MINRF.packetMode; iMIBLEnlights.size()){ + MINRF.activeNlight=0; + MINRF.packetMode=MINRF.firstUsedPacketMode; + } + } + else{ + MINRF.packetMode = (MINRF.packetMode+1>MI_TYPES) ? MINRF.firstUsedPacketMode : MINRF.packetMode+1; + for (uint32_t i = MINRF.packetMode; i 0) { + if (XdrvMailbox.data_len==12){ // a MAC-string + uint8_t _mac[6] = {0}; + MINRFMACStringToBytes(XdrvMailbox.data, _mac); + Response_P(S_JSON_NRF_COMMAND, command, XdrvMailbox.data); + MINRFaddNlight(_mac); + } } break; case CMND_NRF_CHAN: @@ -1302,6 +1364,13 @@ void MINRFShow(bool json) WSContentSend_PD(HTTP_MINRF_MAC, F("Beacon"), D_MAC_ADDRESS, MINRF.beacon.mac[0], MINRF.beacon.mac[1],MINRF.beacon.mac[2],MINRF.beacon.mac[3],MINRF.beacon.mac[4],MINRF.beacon.mac[5]); WSContentSend_PD(PSTR("{s}Beacon Time{m}%u seconds{e}"),MINRF.beacon.time); } + + for(uint32_t i=0; i3) { _page++; counter = 0;