import struct from Crypto.Hash import CMAC from Crypto.Cipher import AES def add_mac(key, trip_cnt, reset_cnt, msg_cnt, msg): # TODO: clean up conversion to and from hex addr, payload, bus = msg reset_flag = reset_cnt & 0b11 msg_cnt_flag = msg_cnt & 0b11 payload = payload[:4] # Step 1: Build Freshness Value (48 bits) # [Trip Counter (16 bit)][[Reset Counter (20 bit)][Message Counter (8 bit)][Reset Flag (2 bit)][Padding (2 bit)] freshness_value = struct.pack('>HI', trip_cnt, (reset_cnt << 12) | ((msg_cnt & 0xff) << 4) | (reset_flag << 2)) # Step 2: Build data to authenticate (96 bits) # [Message ID (16 bits)][Payload (32 bits)][Freshness Value (48 bits)] to_auth = struct.pack('>H', addr) + payload + freshness_value # Step 3: Calculate CMAC (28 bit) cmac = CMAC.new(key, ciphermod=AES) cmac.update(to_auth) mac = cmac.digest().hex()[:7] # truncated MAC # Step 4: Build message # [Payload (32 bit)][Message Counter Flag (2 bit)][Reset Flag (2 bit)][Authenticator (28 bit)] msg_cnt_rst_flag = struct.pack('>B', (msg_cnt_flag << 2) | reset_flag).hex()[1] msg = payload.hex() + msg_cnt_rst_flag + mac payload = bytes.fromhex(msg) return (addr, payload, bus) def build_sync_mac(key, trip_cnt, reset_cnt, id_=0xf): id_ = struct.pack('>H', id_) # 16 trip_cnt = struct.pack('>H', trip_cnt) # 16 reset_cnt = struct.pack('>I', reset_cnt << 12)[:-1] # 20 + 4 padding to_auth = id_ + trip_cnt + reset_cnt # SecOC 11.4.1.1 page 138 cmac = CMAC.new(key, ciphermod=AES) cmac.update(to_auth) msg = "0" + cmac.digest().hex()[:7] msg = bytes.fromhex(msg) return struct.unpack('>I', msg)[0]