![](/media/cache/img/default_profile.jpg.50x50_q85.jpg)
2018-10-22: This is a temporary repository to fix issue mbed OS issue 8344. I'm reverting to an earlier mbed revision that isn't messed up. Expect mbed to fix the linker issue in the next release and I'll remove this repository. I havne't tested the code at this revision - sorry!
Dependencies: BLE_API mbed mbedtls nRF51822
main.cpp
- Committer:
- electronichamsters
- Date:
- 2017-07-29
- Revision:
- 11:d5edb6e3edab
- Parent:
- 10:9db2ac7e1eea
- Child:
- 12:30c6e83f0fe5
File content as of revision 11:d5edb6e3edab:
/* Eric Tsai 7/29/2017: Added spoof checking using clock algorithm. */ #include "mbed.h" #include "ble/BLE.h" #include "mbedtls/aes.h" //comment out when done with debug uart, else eats batteries #define MyDebugEnb 0 //DigitalOut led1(LED1, 1); Ticker ticker; const uint16_t Periodicity = 180; //clock periodicity used for spoof checking, should be 1800 seconds for 30minutes //aes stuff uint8_t src_buf[16] = {0x1,0x2,0x3,0x4,0x1,0x2,0x3,0x4,0x1,0x2,0x3,0x4,0x1,0x2,0x3,0x4}; uint8_t des_buf[16] = {0x1,0x2,0x3,0x4,0x1,0x2,0x3,0x4,0x1,0x2,0x3,0x4,0x1,0x2,0x3,0x4}; mbedtls_aes_context aes; unsigned char iv[16] = {0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x1, 0x2}; //16-byte key size_t input_len = 16; size_t output_len = 0; Serial device(p9, p11); //nRF51822 uart : TX=p9. RX=p11 //data structures for tracking wake time of BLE end nodes, for to prevent spoofing struct MAC_Birth_Record { uint8_t MAC_Addr[6]; //mac[0] is low order byte in mac address uint16_t MAC_Time; //The time of birth for this end node uint16_t Attempt_MAC_Time; uint8_t Attempt_Cnt; uint8_t Xmit_Cnt; }; const uint8_t sizeOfSpoof = 50; MAC_Birth_Record Spoof_Check[sizeOfSpoof]; //array tracking end node birth times uint8_t Spoof_Ray_Tail = 0; //array index for next record uint8_t Received_MAC_Addr[6]; //mac[0] is low order byte in mac address uint16_t Received_MAC_Time; static Timer Gateway_Time; static uint16_t Expected_MAC_Time; uint8_t Received_Xmit_Cnt; const uint8_t ADVisUnknown = 0; const uint8_t ADVisNew = 1; const uint8_t ADVisDuplicate = 2; const uint8_t ADVisSpoof = 3; //******************** // Takes into account 30-minute clock, wrap arounds and edge cases // returns: // TRUE: if sensor's reported time lines up to gateway's recorded time // Expected_MAC_Time //******************* bool Is_Birth_Time_Correct (uint16_t gateway_mac_time, uint16_t sensor_mac_time, uint8_t margin) { bool return_val = 0; uint16_t current_time = (uint16_t)(Gateway_Time.read_ms()/1000); int16_t gateway_time_zone = current_time - sensor_mac_time; if (current_time >= sensor_mac_time) { Expected_MAC_Time = current_time - sensor_mac_time; } else { Expected_MAC_Time = (Periodicity - sensor_mac_time + current_time); } if (1) //check for count==0 situations { if ( (gateway_mac_time < (Expected_MAC_Time + margin)) && (gateway_mac_time > (Expected_MAC_Time - margin)) ) { return_val = 1; } else { return_val = 0; } } /* //if positive, not a wrap around edge case //todo: take into account low count, indicates reset? if ((gateway_time_zone >= 0) && (1)) { Expected_MAC_Time = gateway_time_zone; if ( (gateway_mac_time < (Expected_MAC_Time + margin)) && (gateway_mac_time > (Expected_MAC_Time - margin)) ) { return_val = 1; //valid birth time } } else //wrap around edge case, gateway_time_zone is negative { gateway_time_zone = (Periodicity - sensor_mac_time + current_time); Expected_MAC_Time = gateway_time_zone; //todo: 2 declarations?? //if ( (Periodicity-gateway_mac_time)+current_time == sensor_mac_time) //wrong, need bands if ( (gateway_mac_time < (Expected_MAC_Time + margin)) && (gateway_mac_time > (Expected_MAC_Time - margin)) ) { return_val = 1; } else { return_val = 0; } } */ return return_val; }//end Is_Birth_Time_Correct /* ************************************** check adv against birth records to detect spoofing add device to birth records if new returns TRUE: if device reported birth time matches real birth time if device is first encountered if X consecutive birth time matches FALSE: if device reported birth time does not match ToDo: add attempts ToDo: wrap around of gateway clock not accounted for. Like if Gateway_Time.read_ms() - Received_MAC_Time < 0 */ uint8_t Is_Not_Spoofed () { uint8_t return_val = ADVisUnknown; //iterate through all of birth records looking for one that matches MAC address for (int i=0; i<sizeOfSpoof; i++) { //Search for matching MAC address if (Spoof_Check[i].MAC_Addr[0] == Received_MAC_Addr[0] && Spoof_Check[i].MAC_Addr[1] == Received_MAC_Addr[1] && Spoof_Check[i].MAC_Addr[2] == Received_MAC_Addr[2] && Spoof_Check[i].MAC_Addr[3] == Received_MAC_Addr[3] && Spoof_Check[i].MAC_Addr[4] == Received_MAC_Addr[4] && Spoof_Check[i].MAC_Addr[5] == Received_MAC_Addr[5]) { #if MyDebugEnb device.printf("found MAC address in array\r\n"); device.printf(" Index = %d \r\n", i); device.printf(" MAC = %02x:%02x:%02x:%02x:%02x:%02x \r\n", Received_MAC_Addr[5],Received_MAC_Addr[4],Received_MAC_Addr[3],Received_MAC_Addr[2],Received_MAC_Addr[1],Received_MAC_Addr[0]); device.printf(" Received MAC Time = %d \r\n", Received_MAC_Time); device.printf(" Gateway time = %d \r\n", (uint16_t)(Gateway_Time.read_ms()/1000)); device.printf(" Array.MAC_Time = %d \r\n", Spoof_Check[i].MAC_Time); device.printf(" Array.Attempt_MAC_Time = %d \r\n", Spoof_Check[i].Attempt_MAC_Time); device.printf(" Array.Attempt_Cnt = %d \r\n", Spoof_Check[i].Attempt_Cnt); device.printf(" Array.Xmit_Cnt = %d \r\n", Spoof_Check[i].Xmit_Cnt); device.printf(" expected time = %d \r\n", ((uint16_t)(Gateway_Time.read_ms()/1000)) - Received_MAC_Time); #endif //calcuate the time of birth of this bluetooth module @ gateway time zone relative to received MAC_Time. //int16_t expected_mac_time = ((uint16_t)(Gateway_Time.read_ms()/1000)) - Received_MAC_Time; //uint16_t expected_mac_time = 0; //todo: this needs to be a POSITIVE VALUE, calculated by Is_MAC_Time_Correct(); // ToDo: there's a bug here. Need to use count to figure out when it's a first-powered on device. Can't use time? //***** // check primary Mac_Time //**** if ( Is_Birth_Time_Correct(Spoof_Check[i].MAC_Time, Received_MAC_Time, 3) ) { if (Spoof_Check[i].Xmit_Cnt != Received_Xmit_Cnt) { return_val = ADVisNew; //MAC Time checks out and not duplicate Spoof_Check[i].MAC_Time = Expected_MAC_Time; //update birth time for this device. Spoof_Check[i].Xmit_Cnt = Received_Xmit_Cnt; //i = sizeOfSpoof; //exit for loop, we've found the MAC address. But can't do it this way. //ToDo: hey, what happens if the array holds entries w/ same MAC? Is that possible? #if MyDebugEnb device.printf("Pirmary MAC Time as expected...expected=%d...Spoof_mac_tmr=%d \r\n", Expected_MAC_Time, Spoof_Check[i].MAC_Time); #endif } else { return_val = ADVisDuplicate; //MAC Time checks out and is duplicate } }//if primary MAC_time match else { //***** // check secondary Mac_Time //**** #if MyDebugEnb device.printf(" MAC Time No Match!!...expected=%d...Spoof_mac_tmr=%d \r\n", Expected_MAC_Time, Spoof_Check[i].MAC_Time); #endif //increment count if matches secondary //if ( (Spoof_Check[i].Attempt_MAC_Time < (expected_mac_time + 5)) && (Spoof_Check[i].Attempt_MAC_Time > (expected_mac_time - 5)) ) if (Is_Birth_Time_Correct(Spoof_Check[i].Attempt_MAC_Time, Received_MAC_Time, 3)) { if (Spoof_Check[i].Xmit_Cnt != Received_Xmit_Cnt) { //Spoof_Check[i].Attempt_MAC_Time = expected_mac_time; Spoof_Check[i].Attempt_Cnt++; Spoof_Check[i].Xmit_Cnt = Received_Xmit_Cnt; #if MyDebugEnb device.printf(" Match on secondary, Attempt_Cnt= %d, Attempt_MAC_Time = %d, Expected_MAC_Time=%d \r\n", Spoof_Check[i].Attempt_Cnt, Spoof_Check[i].Attempt_MAC_Time, Expected_MAC_Time); #endif //this takes care of usecase where a module is reset after it's already been seen by gateway //after 3 consecutive correlated MAC_Time attempts we assume is our long lost friend and not a spoof. if (Spoof_Check[i].Attempt_Cnt >= 3) { return_val = ADVisNew; //promote this MAC_Time to primary and start accepting data @ this MAC_Time Spoof_Check[i].MAC_Time = Expected_MAC_Time; Spoof_Check[i].Xmit_Cnt = Received_Xmit_Cnt; Spoof_Check[i].Attempt_Cnt = 0; } else //Received_MAC_Time matches Attempt, but not enough times to be considered as valid. { return_val = ADVisSpoof; } }//is not duplicate, and transmit count matches else { #if MyDebugEnb device.printf(" is Duplicate , but matches Attempt_MAC_Time matches\r\n"); #endif return_val = ADVisDuplicate; } } //Recevied MAC_Time matches secondary MAC_Time else { #if MyDebugEnb device.printf(" No Match on secondary either, ===setting return_val = Spoof ===\r\n"); #endif return_val = ADVisSpoof; //it should still be zero, so this is just for clarification //at this point: MAC matches, MAC_Time doesn't match primary nor secondary. Spoof_Check[i].Attempt_Cnt = 0; //reset secondary count } //update second backup regardless whether it matches exptected_mac_time matches secondary or not. Spoof_Check[i].Attempt_MAC_Time = Expected_MAC_Time; Spoof_Check[i].Xmit_Cnt = Received_Xmit_Cnt; }//if Primary MAC_Time doesn't match break; //return_val tells us if this is a valid or not. Don't need to search through remainder of array }//if matching MAC address else //MAC doesn't match { //MAC doesn't match and we've searched through entire record //let's add this MAC as new, an initialze records to match this sensor if (i >= (sizeOfSpoof - 1)) //we've searched through entire array and didn't find this MAC { //we've searched through the entire array and didn't find this MAC address //add this MAC to array, and report this as valid MAC_Time #if MyDebugEnb device.printf("MAC not found, creating new at %d \r\n", Spoof_Ray_Tail); device.printf(" sizeOfSpoof=%d ... and i=%d \r\n", sizeOfSpoof, i); #endif return_val = ADVisNew; //create new entry for newly seen MAC @ tail // why refigure?? Spoof_Check[Spoof_Ray_Tail].MAC_Time = ((uint16_t)(Gateway_Time.read_ms()/1000)) - Received_MAC_Time; Spoof_Check[Spoof_Ray_Tail].MAC_Addr[0] = Received_MAC_Addr[0]; Spoof_Check[Spoof_Ray_Tail].MAC_Addr[1] = Received_MAC_Addr[1]; Spoof_Check[Spoof_Ray_Tail].MAC_Addr[2] = Received_MAC_Addr[2]; Spoof_Check[Spoof_Ray_Tail].MAC_Addr[3] = Received_MAC_Addr[3]; Spoof_Check[Spoof_Ray_Tail].MAC_Addr[4] = Received_MAC_Addr[4]; Spoof_Check[Spoof_Ray_Tail].MAC_Addr[5] = Received_MAC_Addr[5]; Spoof_Check[Spoof_Ray_Tail].Xmit_Cnt = Received_Xmit_Cnt; //calculate Expected_MAC_Time Is_Birth_Time_Correct (0, Received_MAC_Time, 3); #if MyDebugEnb device.printf("Expected_MAC_Time should be %d \r\n", Expected_MAC_Time); #endif Spoof_Check[Spoof_Ray_Tail].MAC_Time = Expected_MAC_Time; //wrong!!! Spoof_Check[Spoof_Ray_Tail].Xmit_Cnt = Received_Xmit_Cnt; Spoof_Check[Spoof_Ray_Tail].Attempt_Cnt = 0; //increment tail pointer to next position or wrap around //todo: might not be using the last index position of this array, not sure. Doesn't matter IDK. if (Spoof_Ray_Tail >= (sizeOfSpoof-1) ) //FIFO at end of array, return to beginning { Spoof_Ray_Tail = 0; } else { Spoof_Ray_Tail++; } }//end if loop at end of array }//end else not maching MAC }//loop through Spoof_Check array return return_val; }//end Is_Not_Spoofed() void periodicCallback(void) //every 1800 seconds = 30 minutes, max for ticker. { #if MyDebugEnb device.printf("===== reset timer ===== \r\n"); #endif //led1 = !led1; /* Do blinky on LED1 while we're waiting for BLE events */ //device.printf("periodic 5..."); //device.printf("\r\n"); Gateway_Time.reset(); } //with the current beacon, I see 3 valid advertisements per wake period with ambient other beacons. sometimes 4. //even with 2 phones broadcasting at 10Hz, still see 3 adv's. bool is_ours(const uint8_t * adv_data, const uint8_t * adv_address) { //if ((adv_data[5] == adv_address[3]) && (adv_data[6] == adv_address[2]) && (adv_data[7] == adv_address[1]) && (adv_data[8] == adv_address[0])) if ((adv_data[5] == adv_address[3]) && (adv_data[6] == adv_address[2])) return 1; else return 0; } void advertisementCallback(const Gap::AdvertisementCallbackParams_t *params) { //BLE MAC Address (6 bytes): // params->peerAddr[5] // params->peerAddr[4] // ... // params->peerAddr[0] //RSSI = params->rssi //Payload // params->advertisingData[#<advertisingDataLen] // [<---MY DATA-->] //0x02 0x01 0x06 0x06 0xff D E C ? ? //0 1 2 3 4 5 6 7 8 9 if ( (params->advertisingDataLen) >= 8) { //if one of "ours", these UUID fields will be this value. Arbitrary " //if ( (params->advertisingData[5] == 0x44) && (params->advertisingData[6] == 0x45) && (params->advertisingData[7]==0x43) ) //if (is_ours(params->advertisingData[5],params->advertisingData[6],params->advertisingData[7], params->advertisingData[8], params->peerAddr[3], params->peerAddr[2], params->peerAddr[1], params->peerAddr[0] )) if (is_ours(params->advertisingData, params->peerAddr)) { #if MyDebugEnb device.printf("----------- ADV CAll Back -----------------\r\n "); #endif //************** // Decrypt data //************** //prep array for deciphering the encrypted portion of advertisement //device.printf("\r\n copy adv length: %d \r\n", params->advertisingDataLen); //always 31 for (int i = 0; i<16; i++) { src_buf[i]=params->advertisingData[i+15]; #if MyDebugEnb //device.printf("%x ", src_buf[i]); #endif } #if MyDebugEnb //device.printf("...\r\n "); #endif mbedtls_aes_crypt_ecb( &aes, MBEDTLS_AES_DECRYPT, src_buf, des_buf ); //execution time not an issue #if MyDebugEnb //device.printf("decoded first 16 bytes \r\n"); for (int i = 0; i<16; i++) { //device.printf("%02x", params->advertisingData[index]); if (i < 2) { //device.printf("%x ", des_buf[i]); } else { //device.printf("%c ", des_buf[i]); } } //device.printf("done----- \r\n"); #endif //save MAC address Received_MAC_Addr[0] = params->peerAddr[0]; Received_MAC_Addr[1] = params->peerAddr[1]; Received_MAC_Addr[2] = params->peerAddr[2]; Received_MAC_Addr[3] = params->peerAddr[3]; Received_MAC_Addr[4] = params->peerAddr[4]; Received_MAC_Addr[5] = params->peerAddr[5]; uint16_t beacon_timer = des_buf[0] | (des_buf[1] << 8); //it's a 2 byte uint little endian Received_MAC_Time = beacon_timer; Received_Xmit_Cnt = des_buf[2]; /* BLE Received from MAC C2F154BB0AF9 volt:3.11,mag:1 uart-transmit: mac:C2F154BB0AF9,rssi:##,volt:3.11,mag:1 /ble/C2F154BB0AF9/rssi /ble/C2F154BB0AF9/1st_token /ble/c2F153 */ /* device.printf("Adv peerAddr: [%02x%02x%02x%02x%02x%02x] rssi %d, ScanResp: %u, AdvType: %u\r\n", params->peerAddr[5], params->peerAddr[4], params->peerAddr[3], params->peerAddr[2], params->peerAddr[1], params->peerAddr[0], params->rssi, params->isScanResponse, params->type); */ // punch out the data as json //--------------------------------------- // 1. MAC and RSSI // "mac":xxxxxxxx,rssi:xx, //--------------------------------------- uint8_t ADV_Result = Is_Not_Spoofed(); #if MyDebugEnb device.printf("--------ADV_Result = %d", ADV_Result); #endif if (ADV_Result == ADVisNew) { device.printf("{\"mac\":\"%02x%02x%02x%02x%02x%02x\",\"rssi\":%d,", params->peerAddr[5], params->peerAddr[4], params->peerAddr[3], params->peerAddr[2], params->peerAddr[1], params->peerAddr[0], params->rssi ); //mac:"c2f154bb0af9" //--------------------------------------- // 2. Volt // "volt":3.03, //--------------------------------------- device.printf("\"volt\":%c%c%c%c,", params->advertisingData[9], params->advertisingData[10], params->advertisingData[11], params->advertisingData[12]); //--------------------------------------- // 3. clock // "tmr":xxx, //--------------------------------------- //print clock // "tmr":3232, //--------------------------------------- device.printf("\"tmr\":%d,", beacon_timer); //device.printf("\"tmr\":%d,", des_buf[0]); device.printf("\"xmit_cnt\":%d,", Received_Xmit_Cnt); //--------------------------------------- // 4. rest of payload as json // "tmr":xxx, //--------------------------------------- for (int i = 3; i< 16; i++) { //device.printf("%02x", params->advertisingData[index]); device.printf("%c", des_buf[i]); //print as character } /* for (unsigned index = 8; index < params->advertisingDataLen; index++) { //device.printf("%02x", params->advertisingData[index]); device.printf("%c", params->advertisingData[index]); } */ device.printf("}"); device.printf("\r\n"); /* mac:c2f154bb0af9,rssi:-55,volt:3.05,mag:1 mac:c2f154bb0af9,rssi:-67,volt:3.05,mag:1 mac:c2f154bb0af9,rssi:-60,volt:3.07,mag:0 */ } if (ADV_Result == ADVisDuplicate) { device.printf("{\"mac\":\"%02x%02x%02x%02x%02x%02x\",\"duplicate\": %d}\r\n", params->peerAddr[5], params->peerAddr[4], params->peerAddr[3], params->peerAddr[2], params->peerAddr[1], params->peerAddr[0], ADV_Result); } if (ADV_Result == ADVisSpoof) { device.printf("{\"mac\":\"%02x%02x%02x%02x%02x%02x\",\"spoof\": %d}\r\n", params->peerAddr[5], params->peerAddr[4], params->peerAddr[3], params->peerAddr[2], params->peerAddr[1], params->peerAddr[0], ADV_Result); } if (ADV_Result == ADVisUnknown) { device.printf("{\"mac\":\"%02x%02x%02x%02x%02x%02x\",\"unknown\": %d}\r\n", params->peerAddr[5], params->peerAddr[4], params->peerAddr[3], params->peerAddr[2], params->peerAddr[1], params->peerAddr[0], ADV_Result); } }//end if it's our adv }//end if advertisingDataLen }//end advertisementCallback /** * This function is called when the ble initialization process has failed */ void onBleInitError(BLE &ble, ble_error_t error) { /* Initialization error handling should go here */ device.printf("periodic callback "); device.printf("\r\n"); } /** * Callback triggered when the ble initialization process has finished */ void bleInitComplete(BLE::InitializationCompleteCallbackContext *params) { BLE& ble = params->ble; ble_error_t error = params->error; if (error != BLE_ERROR_NONE) { /* In case of error, forward the error handling to onBleInitError */ onBleInitError(ble, error); return; } /* Ensure that it is the default instance of BLE */ if(ble.getInstanceID() != BLE::DEFAULT_INSTANCE) { return; } // in ms. Duty cycle = (interval / window); 200ms/500ms = 40% ble.gap().setScanParams(500 /* scan interval */, 200 /* scan window */); ble.gap().startScan(advertisementCallback); } int main(void) { for (int i=0; i<sizeOfSpoof; i++) { Spoof_Check[i].MAC_Addr[0]= 0; Spoof_Check[i].MAC_Addr[1]= 0; Spoof_Check[i].MAC_Addr[2]= 0; Spoof_Check[i].MAC_Time = 0; Spoof_Check[i].Attempt_MAC_Time = 0; Spoof_Check[i].Attempt_Cnt = 0; Spoof_Check[i].Attempt_Cnt = 0; Spoof_Check[i].Xmit_Cnt = 0; } Gateway_Time.start(); device.baud(9600); device.printf("started main 06 hashed observer... "); device.printf("\r\n"); ticker.attach(periodicCallback, Periodicity); //mbedtls_aes_init(&aes); mbedtls_aes_setkey_dec( &aes, iv, 128 ); //mbedtls_aes_crypt_ecb( &aes, MBEDTLS_AES_DECRYPT, input, output ); BLE &ble = BLE::Instance(); ble.init(bleInitComplete); while (true) { ble.waitForEvent(); } }