xref: /btstack/tool/dump_keys.py (revision 5393bc3bd1457c8100ee47ad0bc4bfec8160622e)
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