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 ]