crypto.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. #!/usr/bin/env python3
  2. import struct, binascii
  3. from .wifi import *
  4. #from binascii import a2b_hex
  5. #from struct import unpack,pack
  6. from Crypto.Cipher import AES, ARC4
  7. from scapy.layers.dot11 import Dot11, Dot11CCMP, Dot11QoS
  8. import zlib
  9. def pn2bytes(pn):
  10. pn_bytes = [0] * 6
  11. for i in range(6):
  12. pn_bytes[i] = pn & 0xFF
  13. pn >>= 8
  14. return pn_bytes
  15. def pn2bin(pn):
  16. return struct.pack(">Q", pn)[2:]
  17. def dot11ccmp_get_pn(p):
  18. pn = p.PN5
  19. pn = (pn << 8) | p.PN4
  20. pn = (pn << 8) | p.PN3
  21. pn = (pn << 8) | p.PN2
  22. pn = (pn << 8) | p.PN1
  23. pn = (pn << 8) | p.PN0
  24. return pn
  25. def ccmp_get_nonce(priority, addr, pn):
  26. return struct.pack("B", priority) + addr2bin(addr) + pn2bin(pn)
  27. def ccmp_get_aad(p, amsdu_spp=False):
  28. # FC field with masked values
  29. fc = raw(p)[:2]
  30. fc = struct.pack("<BB", fc[0] & 0x8f, fc[1] & 0xc7)
  31. # Sequence number is masked, but fragment number is included
  32. sc = struct.pack("<H", p.SC & 0xf)
  33. addr1 = addr2bin(p.addr1)
  34. addr2 = addr2bin(p.addr2)
  35. addr3 = addr2bin(p.addr3)
  36. aad = fc + addr1 + addr2 + addr3 + sc
  37. if Dot11QoS in p:
  38. if not amsdu_spp:
  39. # Everything except the TID is masked
  40. aad += struct.pack("<H", p[Dot11QoS].TID)
  41. else:
  42. # TODO: Mask unrelated fields
  43. aad += raw(p[Dot11QoS])[:2]
  44. return aad
  45. def Raw(x):
  46. return x
  47. def encrypt_ccmp(p, tk, pn, keyid=0, amsdu_spp=False):
  48. """Takes a plaintext Dot11 frame, encrypts it, and adds all the necessairy headers"""
  49. # Update the FC field
  50. p = p.copy()
  51. p.FCfield |= Dot11(FCfield="protected").FCfield
  52. if Dot11QoS in p:
  53. payload = raw(p[Dot11QoS].payload)
  54. p[Dot11QoS].remove_payload()
  55. # Explicitly set TID so we can assume it's an integer
  56. if p[Dot11QoS].TID == None:
  57. p[Dot11QoS].TID = 0
  58. priority = p[Dot11QoS].TID
  59. else:
  60. payload = raw(p.payload)
  61. p.remove_payload()
  62. priority = 0
  63. # Add the CCMP header. res0 and res1 are by default set to zero.
  64. newp = p/Dot11CCMP()
  65. pn_bytes = pn2bytes(pn)
  66. newp.PN0, newp.PN1, newp.PN2, newp.PN3, newp.PN4, newp.PN5 = pn_bytes
  67. newp.key_id = keyid
  68. newp.ext_iv = 1
  69. # Generate the CCMP Header and AAD for encryption.
  70. ccm_nonce = ccmp_get_nonce(priority, newp.addr2, pn)
  71. ccm_aad = ccmp_get_aad(newp, amsdu_spp)
  72. #print("CCM Nonce:", ccm_nonce.hex())
  73. #print("CCM aad :", ccm_aad.hex())
  74. # Encrypt the plaintext using AES in CCM Mode.
  75. #print("Payload:", payload.hex())
  76. cipher = AES.new(tk, AES.MODE_CCM, ccm_nonce, mac_len=8)
  77. cipher.update(ccm_aad)
  78. ciphertext = cipher.encrypt(payload)
  79. digest = cipher.digest()
  80. newp = newp/Raw(ciphertext)
  81. newp = newp/Raw(digest)
  82. #print("Ciphertext:", ciphertext.hex())
  83. #print(repr(newp))
  84. #print(raw(newp).hex())
  85. return newp
  86. def decrypt_ccmp(p, tk, verify=True):
  87. """Takes a Dot11CCMP frame and decrypts it"""
  88. p = p.copy()
  89. # Get used CCMP parameters
  90. keyid = get_ccmp_keyid(p)
  91. priority = dot11_get_priority(p)
  92. pn = dot11ccmp_get_pn(p)
  93. # TODO: Mask flags in p.FCfield that are not part of the AAD
  94. fc = p.FCfield
  95. payload = get_ccmp_payload(p)
  96. if Dot11QoS in p:
  97. p[Dot11QoS].remove_payload()
  98. else:
  99. p.remove_payload()
  100. # Prepare for CCMP decryption
  101. ccm_nonce = ccmp_get_nonce(priority, p.addr2, pn)
  102. ccm_aad = ccmp_get_aad(p)
  103. # Decrypt using AES in CCM Mode.
  104. cipher = AES.new(tk, AES.MODE_CCM, ccm_nonce, mac_len=8)
  105. cipher.update(ccm_aad)
  106. plaintext = cipher.decrypt(payload[:-8])
  107. try:
  108. if verify:
  109. cipher.verify(payload[-8:])
  110. except ValueError:
  111. return None
  112. return p/LLC(plaintext)
  113. def encrypt_wep(p, key, pn, keyid=0):
  114. """Takes a plaintext Dot11 frame, encrypts it, and adds all the necessairy headers"""
  115. # Update the FC field --- XXX share this with encrypt_ccmp
  116. p = p.copy()
  117. p.FCfield |= Dot11(FCfield="protected").FCfield
  118. if Dot11QoS in p:
  119. payload = raw(p[Dot11QoS].payload)
  120. p[Dot11QoS].remove_payload()
  121. # Explicitly set TID so we can assume it's an integer
  122. if p[Dot11QoS].TID == None:
  123. p[Dot11QoS].TID = 0
  124. priority = p[Dot11QoS].TID
  125. else:
  126. payload = raw(p.payload)
  127. p.remove_payload()
  128. priority = 0
  129. # Add the WEP ICV which will be encrypted
  130. payload += struct.pack("<I", zlib.crc32(payload) & 0xffffffff)
  131. iv = struct.pack(">I", pn)[1:]
  132. cipher = ARC4.new(iv + key)
  133. ciphertext = cipher.encrypt(payload)
  134. # Construct packet ourselves to avoid scapy bugs
  135. newp = p/iv/struct.pack("<B", keyid)/ciphertext
  136. return newp