import struct from crccheck.crc import Crc32Mpeg2 class Keybox: def __init__(self, data): length = len(data) if length not in (128, 132): raise ValueError(f"Invalid keybox length: {length}. Should be 128 or 132 bytes") if length == 128: # QSEE style keybox if data[0x80:0x84] != b"LVL1": raise ValueError("QSEE style keybox does not end in bytes 'LVL1'") data = data[0:0x80] if data[0x78:0x7C] != b"kbox": raise ValueError("Invalid keybox magic") body_crc = Crc32Mpeg2.calc(data[:0x7C]) body_crc_expected = struct.unpack(">L", data[0x7C:0x7C + 4])[0] if body_crc_expected != body_crc: raise ValueError(f"Keybox CRC is bad. Expected: 0x{body_crc_expected:08X}. Computed: 0x{body_crc:08X}") self.stable_id = data[0x00:0x20] # aka device ID self.device_aes_key = data[0x20:0x30] self.device_id = data[0x30:0x78] # device id sent to google, possibly flags + system_id + encrypted # known fields self.flags, self.system_id = struct.unpack(">L", self.device_id[0:8])[:2] def __str__(self): return f"{self.stable_id.decode('utf-8').strip()} ({self.system_id})" def __repr__(self): return "{name}({items})".format( name=self.__class__.__name__, items=", ".join([f"{k}={repr(v)}" for k, v in self.__dict__.items()]) ) @classmethod def load(cls, file): """Load Keybox from a file path.""" with open(file, "rb", encoding="utf-8") as fd: return cls(fd.read())