Need assistance AES-CCM decryption of an 802.11 payload using Python pycryptodome does not pass integrity check

Viewed 197

I've been going by the 802.11-2020 standard document to code up calculating the pairwise transient key (PTK) of a client joining a known network (SSID/PSK). I've validated the PTK through debugs of wpa_supplicant (-dd -K switches) on a Linux box. So, given the correct key and generating the nonce and AAD from information in the 802.11/CCMP headers, I should be able to drop the last 4 bytes (FCS) take the next to last 8 bytes as the MAC and decrypt the payload and verify using the MAC. My code fails the integrity check of either the decrypt_and_verify() or decrypt() followed by verify(). However, the output of decrypt() is in fact the clear text frame payload with padded 0's. If I re-encrypt that, I get the same encrypted data but with a different MAC. So, I'm trying to figure out why it's failing the original integrity check as well as why the decrypted data comes back padded with zeros. In Annex J of the standard document, there is sample data and my code works just fine for the sample data. Here's my code with hardcoded sample data from my lab (need pycryptodome and latest scapy from github):

import binascii
from Crypto.Cipher import AES
from scapy.all import *

#hardcoded data from lab setup
key = binascii.a2b_hex('43e3229c41fec8fb81222388c0b5d3d3')
nonce = binascii.a2b_hex('03380e4dc29a0d000000000001')
aad = binascii.a2b_hex('8842687f748e4979380e4dc29a0d70ca9b3b67ff00000300')
header = binascii.a2b_hex('880a2c00687f748e4979380e4dc29a0d70ca9b3b67ff00000300') #header for constructing clear text frame
payload = binascii.a2b_hex('b1df4bb514f0fe2c8415690ee0f6340cce486bab2ca4188ff0be70432d6d9548c4cd7ca4e49e6b1298b16ec958b453862f3cf582743f77f8b1ab49f41c6d')

#grab actual encrypted payload
cipherdata = payload[:-12]

#grab MAC
tag = payload[len(payload)-12:len(payload)-4]

#decrypt payload
cipher = AES.new(key, AES.MODE_CCM, nonce, mac_len=8, msg_len=len(cipherdata), assoc_len=len(aad))
cipher.update(aad)
data = cipher.decrypt(cipherdata)

#construct RadioTap packet
mypacket = RadioTap()
mypacket.payload = header + data
mypacket.decode_payload_as(Dot11)
mypacket.show() #see a nice ARP frame
#wireshark(mypacket) #uncomment to see frame in Wireshark

try:
    cipher.verify(tag)
except Exception as ex:
    print(ex)

#reencrypt clear data
cipher2 = AES.new(key, AES.MODE_CCM, nonce, mac_len=8, msg_len=len(data), assoc_len=len(aad))
cipher2.update(aad)
cipherdata2, tag2 = cipher2.encrypt_and_digest(data)

print(f'cipherdata = {cipherdata.hex()}')
print(f'tag = {tag.hex()}')
print(f'cipherdata2 = {cipherdata2.hex()}') #will be the same as cipherdata
print(f'tag2 = {tag2.hex()}')   #will be different than tag
0 Answers
Related