text: # -*- coding: iso-8859-1 -*-
  """ crypto.cipher.tkip_encr
  
      TKIP encryption from IEEE 802.11 TGi
  
      TKIP uses WEP (ARC4 with crc32) and key mixing
      This is only the encryption and not the Michael integrity check!
  
      Copyright © (c) 2002 by Paul A. Lambert
      Read LICENSE.txt for license information.
  
      November 2002
  """
  from crypto.cipher.arc4 import ARC4
  from zlib   import crc32
  from struct import pack
  from crypto.keyedHash.tkip_key_mixing import TKIP_Mixer
  from crypto.errors import BadKeySizeError, IntegrityCheckError
  from binascii_plus import *
  
  
  class TKIP_encr:
      """ TKIP Stream Cipher Algorithm without the Michael integrity check
  
          TKIP encryption on an MPDU using WEP with a longer 'iv'
          and the TKIP key mixing algorithm .  This does NOT include
          the Michael integrity algorithm that operates on the MSDU data.
      """
  
  
      def __init__(self, key=None, transmitterAddress=None, keyID=None):
          """ Initialize TKIP_encr, key -> octet string for key """
          assert(keyID == 0 or keyID == None), 'keyID should be zero in TKIP'
          self.keyId = 0
          self.name = 'TKIP_encr'
          self.strength = 128
          self.encryptHeaderSize = 8  # used to skip octets on decrypt
          self.arc4 = ARC4()  # base algorithm
          self.keyMixer = TKIP_Mixer(key, transmitterAddress)
          if key != None:  # normally in base init, uses adjusted keySize
              self.setKey(key)
          if transmitterAddress != None:
              self.setTA(transmitterAddress)
  
  
      def setKey(self, key, ta=None):
          """ Set key, key string is 16 octets long """
          self.keyMixer.setKey(key)
          if ta != None:
              self.setTA(ta)
  
  
      def setTA(self, transmitterAddress):
          """ Set the transmitter address """
          self.keyMixer.setTA(transmitterAddress)
  
  
      def _getIVandKeyID(self, cipherText):
          """ Parse the TKIP header to get iv and set KeyID
              iv is returned as octet string and is little-endian!!!
          """
          assert(ord(cipherText[3]) & 0x20), 'extIV SHOULD be set in TKIP header'
          self.setCurrentKeyID = (ord(cipherText[3]) & 0xC0) >> 6
          return cipherText[:3] + cipherText[5:9]  # note iv octets are little-endian!!!
  
  
      def _makeARC4key(self, tscOctets, keyID=0):
          """ Make an ARC4 key from TKIP Sequence Counter Octets (little-endian) """
          if keyID != 0 :
              raise 'TKIP expects keyID of zero'
          print "tscOctets in tkmixer=", b2a_p(tscOctets)
          newKey = self.keyMixer.newKey(tscOctets)
          print "newKey=", b2a_p(newKey)
          return newKey
  
  
      def encrypt(self, plainText, iv):
          """ Encrypt a string and return a binary string
              iv is 6 octets of little-endian encoded pn
          """
          assert(len(iv) == 6), 'TKIP bad IV size on encryption'
          self.pnField = iv
          self.arc4.setKey(self._makeARC4key(iv))
          eh1 = chr((ord(iv[0]) | 0x20) & 0x7f)
          encryptionHeader = iv[0] + eh1 + iv[1] + chr((self.keyId << 6) | 0x20) + iv[2:]
          crc = pack('<I', crc32(plainText))
          cipherText = encryptionHeader + self.arc4.encrypt(plainText + crc)
          return cipherText
  
  
      def decrypt(self, cipherText):
          """ Decrypt a WEP packet, assumes WEP 4 byte header on packet """
          assert(ord(cipherText[3]) & 0x20), 'extIV SHOULD be set in TKIP header'
          self.setCurrentKeyID = (ord(cipherText[3]) & 0xC0) >> 6
          iv = cipherText[0] + cipherText[2] + cipherText[4:8]
          self.pnField = iv
          self.arc4.setKey(self._makeARC4key(iv))
          plainText = self.arc4.decrypt(cipherText[self.encryptHeaderSize:])
          if plainText[-4:] != pack('<I', crc32(plainText[:-4])):  # check data integrity
              raise (IntegrityCheckError, 'WEP CRC Integrity Check Error')
          return plainText[:-4]
  
  