fragattacks/research/libwifi/crypto.py

170 lines
4.2 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
import struct, binascii
from .wifi import *
#from binascii import a2b_hex
#from struct import unpack,pack
from Crypto.Cipher import AES, ARC4
from scapy.layers.dot11 import Dot11, Dot11CCMP, Dot11QoS
import zlib
def pn2bytes(pn):
pn_bytes = [0] * 6
for i in range(6):
pn_bytes[i] = pn & 0xFF
pn >>= 8
return pn_bytes
def pn2bin(pn):
return struct.pack(">Q", pn)[2:]
def dot11ccmp_get_pn(p):
pn = p.PN5
pn = (pn << 8) | p.PN4
pn = (pn << 8) | p.PN3
pn = (pn << 8) | p.PN2
pn = (pn << 8) | p.PN1
pn = (pn << 8) | p.PN0
return pn
def ccmp_get_nonce(priority, addr, pn):
return struct.pack("B", priority) + addr2bin(addr) + pn2bin(pn)
def ccmp_get_aad(p, amsdu_spp=False):
# FC field with masked values
fc = raw(p)[:2]
fc = struct.pack("<BB", fc[0] & 0x8f, fc[1] & 0xc7)
# Sequence number is masked, but fragment number is included
sc = struct.pack("<H", p.SC & 0xf)
addr1 = addr2bin(p.addr1)
addr2 = addr2bin(p.addr2)
addr3 = addr2bin(p.addr3)
aad = fc + addr1 + addr2 + addr3 + sc
if Dot11QoS in p:
if not amsdu_spp:
# Everything except the TID is masked
aad += struct.pack("<H", p[Dot11QoS].TID)
else:
# TODO: Mask unrelated fields
aad += raw(p[Dot11QoS])[:2]
return aad
def Raw(x):
return x
def encrypt_ccmp(p, tk, pn, keyid=0, amsdu_spp=False):
"""Takes a plaintext Dot11 frame, encrypts it, and adds all the necessairy headers"""
# Update the FC field
p = p.copy()
p.FCfield |= Dot11(FCfield="protected").FCfield
if Dot11QoS in p:
payload = raw(p[Dot11QoS].payload)
p[Dot11QoS].remove_payload()
# Explicitly set TID so we can assume it's an integer
if p[Dot11QoS].TID == None:
p[Dot11QoS].TID = 0
priority = p[Dot11QoS].TID
else:
payload = raw(p.payload)
p.remove_payload()
priority = 0
# Add the CCMP header. res0 and res1 are by default set to zero.
newp = p/Dot11CCMP()
pn_bytes = pn2bytes(pn)
newp.PN0, newp.PN1, newp.PN2, newp.PN3, newp.PN4, newp.PN5 = pn_bytes
newp.key_id = keyid
newp.ext_iv = 1
# Generate the CCMP Header and AAD for encryption.
ccm_nonce = ccmp_get_nonce(priority, newp.addr2, pn)
ccm_aad = ccmp_get_aad(newp, amsdu_spp)
#print("CCM Nonce:", ccm_nonce.hex())
#print("CCM aad :", ccm_aad.hex())
# Encrypt the plaintext using AES in CCM Mode.
#print("Payload:", payload.hex())
cipher = AES.new(tk, AES.MODE_CCM, ccm_nonce, mac_len=8)
cipher.update(ccm_aad)
ciphertext = cipher.encrypt(payload)
digest = cipher.digest()
newp = newp/Raw(ciphertext)
newp = newp/Raw(digest)
#print("Ciphertext:", ciphertext.hex())
#print(repr(newp))
#print(raw(newp).hex())
return newp
def decrypt_ccmp(p, tk, verify=True):
"""Takes a Dot11CCMP frame and decrypts it"""
p = p.copy()
# Get used CCMP parameters
keyid = p.key_id
priority = dot11_get_priority(p)
pn = dot11ccmp_get_pn(p)
# TODO: Mask flags in p.FCfield that are not part of the AAD
fc = p.FCfield
payload = get_ccmp_payload(p)
if Dot11QoS in p:
p[Dot11QoS].remove_payload()
else:
p.remove_payload()
# Prepare for CCMP decryption
ccm_nonce = ccmp_get_nonce(priority, p.addr2, pn)
ccm_aad = ccmp_get_aad(p)
# Decrypt using AES in CCM Mode.
cipher = AES.new(tk, AES.MODE_CCM, ccm_nonce, mac_len=8)
cipher.update(ccm_aad)
plaintext = cipher.decrypt(payload[:-8])
try:
if verify:
cipher.verify(payload[-8:])
except ValueError:
return None
return p/LLC(plaintext)
def encrypt_wep(p, key, pn, keyid=0):
"""Takes a plaintext Dot11 frame, encrypts it, and adds all the necessairy headers"""
# Update the FC field --- XXX share this with encrypt_ccmp
p = p.copy()
p.FCfield |= Dot11(FCfield="protected").FCfield
if Dot11QoS in p:
payload = raw(p[Dot11QoS].payload)
p[Dot11QoS].remove_payload()
# Explicitly set TID so we can assume it's an integer
if p[Dot11QoS].TID == None:
p[Dot11QoS].TID = 0
priority = p[Dot11QoS].TID
else:
payload = raw(p.payload)
p.remove_payload()
priority = 0
# Add the WEP ICV which will be encrypted
payload += struct.pack("<I", zlib.crc32(payload) & 0xffffffff)
iv = struct.pack(">I", pn)[1:]
cipher = ARC4.new(iv + key)
ciphertext = cipher.encrypt(payload)
# Construct packet ourselves to avoid scapy bugs
newp = p/iv/struct.pack("<B", keyid)/ciphertext
return newp