1*5393bc3bSMatthias Ringwald#!/usr/bin/env python3 2*5393bc3bSMatthias Ringwald# BlueKitchen GmbH (c) 2022 3*5393bc3bSMatthias Ringwald 4*5393bc3bSMatthias Ringwald# parse PacketLogger and dump encryption keys 5*5393bc3bSMatthias Ringwald 6*5393bc3bSMatthias Ringwaldimport sys 7*5393bc3bSMatthias Ringwaldimport datetime 8*5393bc3bSMatthias Ringwaldimport struct 9*5393bc3bSMatthias Ringwald 10*5393bc3bSMatthias Ringwald 11*5393bc3bSMatthias Ringwalddef as_hex(data): 12*5393bc3bSMatthias Ringwald str_list = [] 13*5393bc3bSMatthias Ringwald for byte in data: 14*5393bc3bSMatthias Ringwald str_list.append("{0:02x} ".format(byte)) 15*5393bc3bSMatthias Ringwald return ''.join(str_list) 16*5393bc3bSMatthias Ringwald 17*5393bc3bSMatthias Ringwald 18*5393bc3bSMatthias Ringwalddef as_key(data): 19*5393bc3bSMatthias Ringwald str_list = [] 20*5393bc3bSMatthias Ringwald for byte in data: 21*5393bc3bSMatthias Ringwald str_list.append("{0:02x}".format(byte)) 22*5393bc3bSMatthias Ringwald return ''.join(str_list) 23*5393bc3bSMatthias Ringwald 24*5393bc3bSMatthias Ringwald 25*5393bc3bSMatthias Ringwalddef as_bd_addr(data): 26*5393bc3bSMatthias Ringwald str_list = [] 27*5393bc3bSMatthias Ringwald for byte in data: 28*5393bc3bSMatthias Ringwald str_list.append("{0:02x}".format(byte)) 29*5393bc3bSMatthias Ringwald return ':'.join(str_list) 30*5393bc3bSMatthias Ringwald 31*5393bc3bSMatthias Ringwald 32*5393bc3bSMatthias Ringwalddef read_header(f): 33*5393bc3bSMatthias Ringwald bytes_read = f.read(13) 34*5393bc3bSMatthias Ringwald if bytes_read: 35*5393bc3bSMatthias Ringwald return struct.unpack(">IIIB", bytes_read) 36*5393bc3bSMatthias Ringwald else: 37*5393bc3bSMatthias Ringwald return (-1, 0, 0, 0) 38*5393bc3bSMatthias Ringwald 39*5393bc3bSMatthias Ringwald 40*5393bc3bSMatthias Ringwalddef handle_at_offset(data, offset): 41*5393bc3bSMatthias Ringwald return struct.unpack_from("<H", data, offset)[0]; 42*5393bc3bSMatthias Ringwald 43*5393bc3bSMatthias Ringwald 44*5393bc3bSMatthias Ringwalddef bd_addr_at_offset(data, offset): 45*5393bc3bSMatthias Ringwald peer_addr = reversed(data[offset:offset + 6]) 46*5393bc3bSMatthias Ringwald return as_bd_addr(peer_addr) 47*5393bc3bSMatthias Ringwald 48*5393bc3bSMatthias Ringwald 49*5393bc3bSMatthias Ringwaldclass hci_connection: 50*5393bc3bSMatthias Ringwald 51*5393bc3bSMatthias Ringwald def __init__(self, bd_addr, con_handle): 52*5393bc3bSMatthias Ringwald self.bd_addr = bd_addr 53*5393bc3bSMatthias Ringwald self.con_handle = con_handle; 54*5393bc3bSMatthias Ringwald 55*5393bc3bSMatthias Ringwald 56*5393bc3bSMatthias Ringwalddef connection_for_handle(con_handle): 57*5393bc3bSMatthias Ringwald if con_handle in connections: 58*5393bc3bSMatthias Ringwald return connections[con_handle] 59*5393bc3bSMatthias Ringwald else: 60*5393bc3bSMatthias Ringwald return None 61*5393bc3bSMatthias Ringwald 62*5393bc3bSMatthias Ringwald 63*5393bc3bSMatthias Ringwalddef handle_cmd(packet): 64*5393bc3bSMatthias Ringwald opcode = struct.unpack_from("<H", packet, 0)[0]; 65*5393bc3bSMatthias Ringwald if opcode == 0x201a: 66*5393bc3bSMatthias Ringwald # LE Long Term Key Request Reply 67*5393bc3bSMatthias Ringwald con_handle = handle_at_offset(packet, 3) 68*5393bc3bSMatthias Ringwald conn = connection_for_handle(con_handle) 69*5393bc3bSMatthias Ringwald print("LE LTK for %s - %s" % (conn.bd_addr, as_key(reversed(packet[5:5+16])))) 70*5393bc3bSMatthias Ringwald elif opcode == 0x2019: 71*5393bc3bSMatthias Ringwald # LE Enable Encryption command 72*5393bc3bSMatthias Ringwald con_handle = handle_at_offset(packet, 3) 73*5393bc3bSMatthias Ringwald conn = connection_for_handle(con_handle) 74*5393bc3bSMatthias Ringwald print("LE LTK for %s - %s" % (conn.bd_addr, as_key(reversed(packet[15:15+16])))) 75*5393bc3bSMatthias Ringwald elif opcode == 0x040b: 76*5393bc3bSMatthias Ringwald # Link Key Request Reply 77*5393bc3bSMatthias Ringwald bd_addr = bd_addr_at_offset(packet, 3) 78*5393bc3bSMatthias Ringwald print("Link Key for %s - %s" % (bd_addr, as_key(reversed(packet[9:9+16])))) 79*5393bc3bSMatthias Ringwald 80*5393bc3bSMatthias Ringwalddef handle_evt(event): 81*5393bc3bSMatthias Ringwald if event[0] == 0x05: 82*5393bc3bSMatthias Ringwald # Disconnection Complete 83*5393bc3bSMatthias Ringwald con_handle = handle_at_offset(event, 3) 84*5393bc3bSMatthias Ringwald print("Disconnection Complete: handle 0x%04x" % con_handle) 85*5393bc3bSMatthias Ringwald connections.pop(con_handle, None) 86*5393bc3bSMatthias Ringwald elif event[0] == 0x18: 87*5393bc3bSMatthias Ringwald # Link Key Notification 88*5393bc3bSMatthias Ringwald bd_addr = bd_addr_at_offset(packet, 2) 89*5393bc3bSMatthias Ringwald print("Link Key for %s - %s" % (bd_addr, as_key(reversed(packet[8:8+16])))) 90*5393bc3bSMatthias Ringwald elif event[0] == 0x3e: 91*5393bc3bSMatthias Ringwald if event[2] == 0x01: 92*5393bc3bSMatthias Ringwald # LE Connection Complete 93*5393bc3bSMatthias Ringwald con_handle = handle_at_offset(event, 4); 94*5393bc3bSMatthias Ringwald peer_addr = bd_addr_at_offset(event, 8) 95*5393bc3bSMatthias Ringwald connection = hci_connection(peer_addr, con_handle) 96*5393bc3bSMatthias Ringwald connections[con_handle] = connection 97*5393bc3bSMatthias Ringwald print("LE Connection Complete: %s handle 0x%04x" % (peer_addr, con_handle)) 98*5393bc3bSMatthias Ringwald 99*5393bc3bSMatthias Ringwald 100*5393bc3bSMatthias Ringwald# globals 101*5393bc3bSMatthias Ringwaldconnections = {} 102*5393bc3bSMatthias Ringwald 103*5393bc3bSMatthias Ringwaldif len(sys.argv) == 1: 104*5393bc3bSMatthias Ringwald print ('Dump encryptiong keys from PacketLogger trace file') 105*5393bc3bSMatthias Ringwald print ('Copyright 2023, BlueKitchen GmbH') 106*5393bc3bSMatthias Ringwald print ('') 107*5393bc3bSMatthias Ringwald print ('Usage: ', sys.argv[0], 'hci_dump.pklg') 108*5393bc3bSMatthias Ringwald exit(0) 109*5393bc3bSMatthias Ringwald 110*5393bc3bSMatthias Ringwaldinfile = sys.argv[1] 111*5393bc3bSMatthias Ringwald 112*5393bc3bSMatthias Ringwaldwith open (infile, 'rb') as fin: 113*5393bc3bSMatthias Ringwald pos = 0 114*5393bc3bSMatthias Ringwald try: 115*5393bc3bSMatthias Ringwald while True: 116*5393bc3bSMatthias Ringwald (entry_len, ts_sec, ts_usec, type) = read_header(fin) 117*5393bc3bSMatthias Ringwald if entry_len < 0: 118*5393bc3bSMatthias Ringwald break 119*5393bc3bSMatthias Ringwald packet_len = entry_len - 9 120*5393bc3bSMatthias Ringwald if packet_len > 66000: 121*5393bc3bSMatthias Ringwald print ("Error parsing pklg at offset %u (%x)." % (pos, pos)) 122*5393bc3bSMatthias Ringwald break 123*5393bc3bSMatthias Ringwald packet = fin.read(packet_len) 124*5393bc3bSMatthias Ringwald pos = pos + 4 + entry_len 125*5393bc3bSMatthias Ringwald if type == 0x00: 126*5393bc3bSMatthias Ringwald handle_cmd(packet) 127*5393bc3bSMatthias Ringwald elif type == 0x01: 128*5393bc3bSMatthias Ringwald handle_evt(packet) 129*5393bc3bSMatthias Ringwald elif type == 0x02: 130*5393bc3bSMatthias Ringwald pass 131*5393bc3bSMatthias Ringwald elif type == 0x03: 132*5393bc3bSMatthias Ringwald pass 133*5393bc3bSMatthias Ringwald 134*5393bc3bSMatthias Ringwald except TypeError as e: 135*5393bc3bSMatthias Ringwald print(e) 136*5393bc3bSMatthias Ringwald print ("Error parsing pklg at offset %u (%x)." % (pos, pos)) 137