├── kmsRequestUnknown.py ├── README.md ├── rpcBase.py ├── rpcRequest.py ├── kmsRequestV6.py ├── filetimes.py ├── server.py ├── kmsRequestV4.py ├── kmsRequestV5.py ├── kmsPidGenerator.py ├── rpcBind.py ├── timezones.py ├── client.py ├── kmsBase.py ├── dcerpc.py ├── structure.py └── aes.py /kmsRequestUnknown.py: -------------------------------------------------------------------------------- 1 | from kmsBase import kmsBase 2 | 3 | class kmsRequestUnknown(kmsBase): 4 | def getResponse(self): 5 | finalResponse = bytearray() 6 | finalResponse.extend(bytearray(struct.pack(' 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are 6 | # met: 7 | # 8 | # * Redistributions of source code must retain the above copyright 9 | # notice, this list of conditions and the following disclaimer. 10 | # * Redistributions in binary form must reproduce the above copyright 11 | # notice, this list of conditions and the following disclaimer in the 12 | # documentation and/or other materials provided with the distribution. 13 | # 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 15 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 16 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 17 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 18 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 19 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 20 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 22 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 23 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | """Tools to convert between Python datetime instances and Microsoft times. 26 | """ 27 | from datetime import datetime, timedelta, tzinfo 28 | from calendar import timegm 29 | 30 | 31 | # http://support.microsoft.com/kb/167296 32 | # How To Convert a UNIX time_t to a Win32 FILETIME or SYSTEMTIME 33 | EPOCH_AS_FILETIME = 116444736000000000 # January 1, 1970 as MS file time 34 | HUNDREDS_OF_NANOSECONDS = 10000000 35 | 36 | 37 | ZERO = timedelta(0) 38 | HOUR = timedelta(hours=1) 39 | 40 | 41 | class UTC(tzinfo): 42 | """UTC""" 43 | def utcoffset(self, dt): 44 | return ZERO 45 | 46 | def tzname(self, dt): 47 | return "UTC" 48 | 49 | def dst(self, dt): 50 | return ZERO 51 | 52 | 53 | utc = UTC() 54 | 55 | 56 | def dt_to_filetime(dt): 57 | """Converts a datetime to Microsoft filetime format. If the object is 58 | time zone-naive, it is forced to UTC before conversion. 59 | 60 | >>> "%.0f" % dt_to_filetime(datetime(2009, 7, 25, 23, 0)) 61 | '128930364000000000' 62 | 63 | >>> "%.0f" % dt_to_filetime(datetime(1970, 1, 1, 0, 0, tzinfo=utc)) 64 | '116444736000000000' 65 | 66 | >>> "%.0f" % dt_to_filetime(datetime(1970, 1, 1, 0, 0)) 67 | '116444736000000000' 68 | 69 | >>> dt_to_filetime(datetime(2009, 7, 25, 23, 0, 0, 100)) 70 | 128930364000001000 71 | """ 72 | if (dt.tzinfo is None) or (dt.tzinfo.utcoffset(dt) is None): 73 | dt = dt.replace(tzinfo=utc) 74 | ft = EPOCH_AS_FILETIME + (timegm(dt.timetuple()) * HUNDREDS_OF_NANOSECONDS) 75 | return ft + (dt.microsecond * 10) 76 | 77 | 78 | def filetime_to_dt(ft): 79 | """Converts a Microsoft filetime number to a Python datetime. The new 80 | datetime object is time zone-naive but is equivalent to tzinfo=utc. 81 | 82 | >>> filetime_to_dt(116444736000000000) 83 | datetime.datetime(1970, 1, 1, 0, 0) 84 | 85 | >>> filetime_to_dt(128930364000000000) 86 | datetime.datetime(2009, 7, 25, 23, 0) 87 | 88 | >>> filetime_to_dt(128930364000001000) 89 | datetime.datetime(2009, 7, 25, 23, 0, 0, 100) 90 | """ 91 | # Get seconds and remainder in terms of Unix epoch 92 | (s, ns100) = divmod(ft - EPOCH_AS_FILETIME, HUNDREDS_OF_NANOSECONDS) 93 | # Convert to datetime object 94 | dt = datetime.utcfromtimestamp(s) 95 | # Add remainder in as microseconds. Python 3.2 requires an integer 96 | dt = dt.replace(microsecond=(ns100 // 10)) 97 | return dt 98 | 99 | 100 | if __name__ == "__main__": 101 | import doctest 102 | 103 | doctest.testmod() 104 | 105 | -------------------------------------------------------------------------------- /server.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import binascii 3 | import hashlib 4 | import random 5 | import socket 6 | import SocketServer 7 | import struct 8 | import uuid 9 | import rpcBind, rpcRequest 10 | 11 | from dcerpc import MSRPCHeader 12 | from rpcBase import rpcBase 13 | 14 | config = {} 15 | 16 | def main(): 17 | parser = argparse.ArgumentParser() 18 | parser.add_argument("ip", nargs="?", action="store", default="0.0.0.0", help="The IP address to listen on. The default is \"0.0.0.0\" (all interfaces).", type=str) 19 | parser.add_argument("port", nargs="?", action="store", default=1688, help="The network port to listen on. The default is \"1688\".", type=int) 20 | parser.add_argument("-e", "--epid", dest="epid", default=None, help="Use this flag to manually specify an ePID to use. If no ePID is specified, a random ePID will be generated.", type=str) 21 | parser.add_argument("-l", "--lcid", dest="lcid", default=1033, help="Use this flag to manually specify an LCID for use with randomly generated ePIDs. If an ePID is manually specified, this setting is ignored.", type=int) 22 | parser.add_argument("-c", "--client-count", dest="CurrentClientCount", default=26, help="Use this flag to specify the current client count. Default is 26. A number >25 is required to enable activation.", type=int) 23 | parser.add_argument("-a", "--activation-interval", dest="VLActivationInterval", default=120, help="Use this flag to specify the activation interval (in minutes). Default is 120 minutes (2 hours).", type=int) 24 | parser.add_argument("-r", "--renewal-interval", dest="VLRenewalInterval", default=1440 * 7, help="Use this flag to specify the renewal interval (in minutes). Default is 10080 minutes (7 days).", type=int) 25 | parser.add_argument("-v", "--verbose", dest="verbose", action="store_const", const=True, default=False, help="Use this flag to enable verbose output.") 26 | parser.add_argument("-d", "--debug", dest="debug", action="store_const", const=True, default=False, help="Use this flag to enable debug output. Implies \"-v\".") 27 | config.update(vars(parser.parse_args())) 28 | if config['debug']: 29 | config['verbose'] = True 30 | server = SocketServer.TCPServer((config['ip'], config['port']), kmsServer) 31 | server.timeout = 5 32 | print "TCP server listening at %s on port %d." % (config['ip'],config['port']) 33 | server.serve_forever() 34 | 35 | class kmsServer(SocketServer.BaseRequestHandler): 36 | def setup(self): 37 | self.connection = self.request 38 | print "Connection accepted: %s:%d" % (self.client_address[0],self.client_address[1]) 39 | 40 | def handle(self): 41 | while True: 42 | # self.request is the TCP socket connected to the client 43 | try: 44 | self.data = self.connection.recv(1024) 45 | except socket.error, e: 46 | if e[0] == 104: 47 | print "Error: Connection reset by peer." 48 | break 49 | else: 50 | raise 51 | if self.data == '' or not self.data: 52 | print "No data received!" 53 | break 54 | # self.data = bytearray(self.data.strip()) 55 | # print binascii.b2a_hex(str(self.data)) 56 | packetType = MSRPCHeader(self.data)['type'] 57 | if packetType == rpcBase.packetType['bindReq']: 58 | if config['verbose']: 59 | print "RPC bind request received." 60 | handler = rpcBind.handler(self.data, config) 61 | elif packetType == rpcBase.packetType['request']: 62 | if config['verbose']: 63 | print "Received activation request." 64 | handler = rpcRequest.handler(self.data, config) 65 | else: 66 | print "Error: Invalid RPC request type", packetType 67 | break 68 | 69 | handler.populate() 70 | res = str(handler.getResponse()) 71 | self.connection.send(res) 72 | 73 | if packetType == rpcBase.packetType['bindReq']: 74 | if config['verbose']: 75 | print "RPC bind acknowledged." 76 | elif packetType == rpcBase.packetType['request']: 77 | if config['verbose']: 78 | print "Responded to activation request." 79 | break 80 | 81 | def finish(self): 82 | self.connection.close() 83 | print "Connection closed: %s:%d" % (self.client_address[0],self.client_address[1]) 84 | 85 | if __name__ == "__main__": 86 | main() 87 | -------------------------------------------------------------------------------- /kmsRequestV4.py: -------------------------------------------------------------------------------- 1 | import binascii 2 | import struct 3 | import time 4 | from kmsBase import kmsBase 5 | from structure import Structure 6 | 7 | # Rijndael SBox 8 | from aes import AES 9 | subTable = AES.sbox 10 | 11 | # Complex 12 | from tablecomplex import tableComplex 13 | 14 | # Intern Hash 15 | def hasher(hashBuffer): 16 | addRoundKey(hashBuffer, 0) 17 | 18 | for i in range(1,6): 19 | shiftRows(hashBuffer) 20 | mixColumns(hashBuffer) 21 | subbytes(hashBuffer) 22 | shiftRows(hashBuffer) 23 | mixColumns(hashBuffer) 24 | addRoundKey(hashBuffer, i << 12) 25 | 26 | shiftRows(hashBuffer) 27 | 28 | # AddRoundKey (Complex) 29 | def addRoundKey(hash, round): 30 | for i in range(0,16): 31 | hash[i] = tableComplex[hash[i] ^ round ^ (i << 8)] 32 | 33 | # Xor Buffer 34 | def xorBuffer(source, offset, destination): 35 | for i in range(0,16): 36 | destination[i] ^= source[i + offset] 37 | 38 | # Rijndael SBox 39 | def subbytes(pbytes): 40 | for i in range(0,16): 41 | pbytes[i] = subTable[pbytes[i]] 42 | 43 | # Rijndael ShiftRows 44 | def shiftRows(pbytes): 45 | bIn = pbytes[0:16] 46 | for i in range(0,16): 47 | pbytes[i] = bIn[(i + ((i & 3) << 2)) & 0xf] 48 | 49 | # Rijndael MixColumns 50 | def mixColumns(pbytes): 51 | bIn = pbytes[0:16] 52 | for i in range(0,16,4): 53 | pbytes[i] = (mulx2(bIn[i]) ^ mulx3(bIn[i + 1]) ^ bIn[i + 2] ^ bIn[i + 3]) & 0xff 54 | pbytes[i + 1] = (bIn[i] ^ mulx2(bIn[i + 1]) ^ mulx3(bIn[i + 2]) ^ bIn[i + 3]) & 0xff 55 | pbytes[i + 2] = (bIn[i] ^ bIn[i + 1] ^ mulx2(bIn[i + 2]) ^ mulx3(bIn[i + 3])) & 0xff 56 | pbytes[i + 3] = (mulx3(bIn[i]) ^ bIn[i + 1] ^ bIn[i + 2] ^ mulx2(bIn[i + 3])) & 0xff 57 | 58 | # Galois Field MUL x 2 59 | def mulx2(bIn): 60 | bOut = (bIn << 1) 61 | if ((bIn & 0x80) != 0x00): 62 | bOut ^= 0x1B 63 | return bOut 64 | 65 | # Galois Field MUL x 3 66 | def mulx3(bIn): 67 | return (mulx2(bIn) ^ bIn) 68 | 69 | class kmsRequestV4(kmsBase): 70 | class RequestV4(Structure): 71 | commonHdr = () 72 | structure = ( 73 | ('bodyLength1', '> 4 108 | 109 | # Remainding bytes 110 | k = messageSize & 0xf 111 | 112 | # Hash 113 | for i in range(0,j): 114 | xorBuffer(message, i << 4, hashBuffer) 115 | hasher(hashBuffer) 116 | 117 | # Bit Padding 118 | ii = 0 119 | for i in range(j << 4, k + (j << 4)): 120 | lastBlock[ii] = message[i] 121 | ii += 1 122 | lastBlock[k] = 0x80 123 | 124 | xorBuffer(lastBlock, 0, hashBuffer) 125 | hasher(hashBuffer) 126 | 127 | return str(hashBuffer) 128 | 129 | def generateResponse(self, responseBuffer, hash): 130 | bodyLength = len(responseBuffer) + len(hash) 131 | response = self.ResponseV4() 132 | response['response'] = responseBuffer 133 | response['hash'] = hash 134 | response['padding'] = self.getResponsePadding(bodyLength) 135 | 136 | if self.config['debug']: 137 | print "KMS V4 Response:", response.dump() 138 | print "KMS V4 Response Bytes:", binascii.b2a_hex(str(response)) 139 | 140 | return str(response) 141 | 142 | def getResponse(self): 143 | return self.responseData 144 | 145 | 146 | -------------------------------------------------------------------------------- /kmsRequestV5.py: -------------------------------------------------------------------------------- 1 | import aes 2 | import binascii 3 | import hashlib 4 | import random 5 | import struct 6 | from kmsBase import kmsBase 7 | from structure import Structure 8 | 9 | class kmsRequestV5(kmsBase): 10 | class RequestV5(Structure): 11 | class Message(Structure): 12 | commonHdr = () 13 | structure = ( 14 | ('salt', '16s'), 15 | ('encrypted', '236s'), #kmsBase.kmsRequestStruct 16 | ('padding', ':'), 17 | ) 18 | 19 | commonHdr = () 20 | structure = ( 21 | ('bodyLength1', ' 1: 166 | # warning += ("We detected multiple matches for your /etc/localtime. " 167 | # "(Matches where %s)" % matches) 168 | # else: 169 | # warning += "We detected no matches for your /etc/localtime." 170 | # warnings.warn(warning) 171 | # 172 | # return localtime 173 | if len(matches) > 0: 174 | return _tzinfome(matches[0]) 175 | 176 | 177 | def _detect_timezone_php(): 178 | tomatch = (time.tzname[0], time.timezone, time.daylight) 179 | now = datetime.datetime.now() 180 | 181 | matches = [] 182 | for tzname in pytz.all_timezones: 183 | try: 184 | tz = pytz.timezone(tzname) 185 | except IOError: 186 | continue 187 | 188 | try: 189 | indst = tz.localize(now).timetuple()[-1] 190 | 191 | if tomatch == (tz._tzname, -tz._utcoffset.seconds, indst): 192 | matches.append(tzname) 193 | 194 | # pylint: disable-msg=W0704 195 | except AttributeError: 196 | pass 197 | 198 | if len(matches) > 1: 199 | warnings.warn("We detected multiple matches for the timezone, choosing " 200 | "the first %s. (Matches where %s)" % (matches[0], matches)) 201 | return pytz.timezone(matches[0]) 202 | 203 | -------------------------------------------------------------------------------- /client.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import binascii 3 | import datetime 4 | import random 5 | import socket 6 | import string 7 | import struct 8 | import sys 9 | import uuid 10 | import filetimes, rpcBind, rpcRequest 11 | 12 | from dcerpc import MSRPCHeader, MSRPCBindNak 13 | from rpcBase import rpcBase 14 | 15 | config = {} 16 | 17 | def main(): 18 | parser = argparse.ArgumentParser() 19 | parser.add_argument("ip", action="store", help="The IP address or hostname of the KMS host.", type=str) 20 | parser.add_argument("port", nargs="?", action="store", default=1688, help="The port the KMS service is listening on. The default is \"1688\".", type=int) 21 | parser.add_argument("-m", "--mode", dest="mode", choices=["WindowsVista","Windows7","Windows8","Windows81","Office2010","Office2013"], default="Windows7") 22 | parser.add_argument("-v", "--verbose", dest="verbose", action="store_const", const=True, default=False, help="Enable this flag to turn on verbose output.") 23 | parser.add_argument("-d", "--debug", dest="debug", action="store_const", const=True, default=False, help="Enable this flag to turn on debug output. Implies \"-v\".") 24 | config.update(vars(parser.parse_args())) 25 | config['call_id'] = 1 26 | if config['debug']: 27 | config['verbose'] = True 28 | updateConfig() 29 | s = socket.socket() 30 | print "Connecting to %s on port %d..." % (config['ip'], config['port']) 31 | s.connect((config['ip'], config['port'])) 32 | if config['verbose']: 33 | print "Connection successful!" 34 | binder = rpcBind.bind('', config) 35 | RPC_Bind = str(binder.generateRequest()) 36 | if config['verbose']: 37 | print "Sending RPC bind request..." 38 | s.send(RPC_Bind) 39 | try: 40 | bindResponse = s.recv(1024) 41 | except socket.error, e: 42 | if e[0] == 104: 43 | print "Error: Connection reset by peer. Exiting..." 44 | sys.exit() 45 | else: 46 | raise 47 | if bindResponse == '' or not bindResponse: 48 | print "No data received! Exiting..." 49 | sys.exit() 50 | packetType = MSRPCHeader(bindResponse)['type'] 51 | if packetType == rpcBase.packetType['bindAck']: 52 | if config['verbose']: 53 | print "RPC bind acknowledged." 54 | #config['call_id'] += 1 55 | ''' 56 | request = CreateRequest() 57 | requester = rpcRequest.request(request, config) 58 | s.send(request) 59 | response = s.recv(1024) 60 | if config['debug']: 61 | print "Response:", binascii.b2a_hex(response), len(response) 62 | parsed = ReadResponse(response) 63 | ''' 64 | elif packetType == rpcBase.packetType['bindNak']: 65 | print MSRPCBindNak(bindResponse).dump() 66 | sys.exit() 67 | else: 68 | print "Something went wrong." 69 | sys.exit() 70 | 71 | def updateConfig(): 72 | if config['mode'] == 'WindowsVista': 73 | config['RequiredClientCount'] = 25 74 | config['KMSProtocolMajorVersion'] = 4 75 | config['KMSProtocolMinorVersion'] = 0 76 | config['KMSClientLicenseStatus'] = 2 77 | config['KMSClientAppID'] = "55c92734-d682-4d71-983e-d6ec3f16059f" 78 | config['KMSClientSkuID'] = "cfd8ff08-c0d7-452b-9f60-ef5c70c32094" 79 | config['KMSClientKMSCountedID'] = "212a64dc-43b1-4d3d-a30c-2fc69d2095c6" 80 | elif config['mode'] == 'Windows7': 81 | config['RequiredClientCount'] = 25 82 | config['KMSProtocolMajorVersion'] = 4 83 | config['KMSProtocolMinorVersion'] = 0 84 | config['KMSClientLicenseStatus'] = 2 85 | config['KMSClientAppID'] = "55c92734-d682-4d71-983e-d6ec3f16059f" 86 | config['KMSClientSkuID'] = "ae2ee509-1b34-41c0-acb7-6d4650168915" 87 | config['KMSClientKMSCountedID'] = "7fde5219-fbfa-484a-82c9-34d1ad53e856" 88 | elif config['mode'] == 'Windows8': 89 | config['RequiredClientCount'] = 25 90 | config['KMSProtocolMajorVersion'] = 5 91 | config['KMSProtocolMinorVersion'] = 0 92 | config['KMSClientLicenseStatus'] = 2 93 | config['KMSClientAppID'] = "55c92734-d682-4d71-983e-d6ec3f16059f" 94 | config['KMSClientSkuID'] = "458e1bec-837a-45f6-b9d5-925ed5d299de" 95 | config['KMSClientKMSCountedID'] = "3c40b358-5948-45af-923b-53d21fcc7e79" 96 | elif config['mode'] == 'Windows81': 97 | config['RequiredClientCount'] = 25 98 | config['KMSProtocolMajorVersion'] = 6 99 | config['KMSProtocolMinorVersion'] = 0 100 | config['KMSClientLicenseStatus'] = 2 101 | config['KMSClientAppID'] = "55c92734-d682-4d71-983e-d6ec3f16059f" 102 | config['KMSClientSkuID'] = "81671aaf-79d1-4eb1-b004-8cbbe173afea" 103 | config['KMSClientKMSCountedID'] = "cb8fc780-2c05-495a-9710-85afffc904d7" 104 | elif config['mode'] == 'Office2010': 105 | config['RequiredClientCount'] = 5 106 | config['KMSProtocolMajorVersion'] = 4 107 | config['KMSProtocolMinorVersion'] = 0 108 | config['KMSClientLicenseStatus'] = 2 109 | config['KMSClientAppID'] = "59a52881-a989-479d-af46-f275c6370663" 110 | config['KMSClientSkuID'] = "6f327760-8c5c-417c-9b61-836a98287e0c" 111 | config['KMSClientKMSCountedID'] = "e85af946-2e25-47b7-83e1-bebcebeac611" 112 | elif config['mode'] == 'Office2013': 113 | config['RequiredClientCount'] = 5 114 | config['KMSProtocolMajorVersion'] = 5 115 | config['KMSProtocolMinorVersion'] = 0 116 | config['KMSClientLicenseStatus'] = 2 117 | config['KMSClientAppID'] = "0ff1ce15-a989-479d-af46-f275c6370663" 118 | config['KMSClientSkuID'] = "b322da9c-a2e2-4058-9e4e-f59a6970bd69" 119 | config['KMSClientKMSCountedID'] = "e6a6f1bf-9d40-40c3-aa9f-c77ba21578c0" 120 | 121 | def CreateRequestBase(): 122 | # Init requestDict 123 | requestDict = {} 124 | 125 | # KMS Protocol Version 126 | requestDict['MajorVer'] = config['KMSProtocolMajorVersion'] 127 | requestDict['MinorVer'] = config['KMSProtocolMinorVersion'] 128 | 129 | # KMS Client is NOT a VM 130 | requestDict['IsClientVM'] = 0 131 | 132 | # License Status 133 | requestDict['LicenseStatus'] = config['KMSClientLicenseStatus'] 134 | 135 | # Grace Time 136 | requestDict['GraceTime'] = 43200 137 | 138 | # Application ID 139 | requestDict['ApplicationId'] = uuid.UUID(config['KMSClientAppID']) 140 | 141 | # SKU ID 142 | requestDict['SkuId'] = uuid.UUID(config['KMSClientSkuID']) 143 | 144 | # KMS Counted ID 145 | requestDict['KmsCountedId'] = uuid.UUID(config['KMSClientKMSCountedID']) 146 | 147 | # CMID 148 | requestDict['ClientMachineId'] = uuid.uuid4() 149 | 150 | # Minimum Clients 151 | requestDict['RequiredClientCount'] = config['RequiredClientCount'] 152 | 153 | # Current Time 154 | requestDict['RequestTime'] = filetimes.dt_to_filetime(datetime.datetime.utcnow()) 155 | 156 | # Generate Random Machine Name (Up to 63 Characters) 157 | requestDict['MachineName'] = ''.join(random.choice(string.letters + string.digits) for i in range(32)) 158 | 159 | # Debug Stuff 160 | if config['debug']: 161 | print "Request Base Dictionary:", requestDict 162 | 163 | request = str() 164 | request += struct.pack('Q', requestDict['RequestTime']) 175 | request += requestDict['ClientMachineId'].bytes_le 176 | request += requestDict['MachineName'].encode('utf-16le') 177 | request += ('\0' * 32).encode('utf-16le') 178 | if config['debug']: 179 | print "Request Base:", binascii.b2a_hex(request), len(request) 180 | 181 | return request 182 | 183 | def CreateRequestV4(): 184 | # Update the call ID 185 | config['call_id'] += 1 186 | 187 | # Create KMS Client Request Base 188 | requestBase = CreateRequestBase() 189 | 190 | # Create Hash 191 | hashed = str(kmsRequestV4.main(bytearray(requestBase))) 192 | 193 | # Generate Request 194 | bodyLength = len(requestBase) + len(hashed) 195 | if bodyLength % 8 == 0: 196 | paddingLength = 0 197 | else: 198 | paddingLength = 8 - bodyLength % 8 199 | v4Data = { 200 | "BodyLength" : bodyLength, 201 | "BodyLength2" : bodyLength, 202 | "Hash" : hashed, 203 | "Padding" : str(bytearray(functions.arrayFill([], paddingLength, 0x00))) 204 | } 205 | if config['debug']: 206 | print "Request V4 Data:", v4Data 207 | request = str() 208 | request += struct.pack(' 0 else 0)'), 203 | ('pduData',':'), 204 | ('_pad', '_-pad','(4 - ((self._SIZE + len(self["pduData"])) & 3) & 3)'), 205 | ('pad', ':'), 206 | ('_sec_trailer', '_-sec_trailer', '8 if self["auth_len"] > 0 else 0'), 207 | ('sec_trailer',':'), 208 | ('auth_dataLen','_-auth_data','self["auth_len"]'), 209 | ('auth_data',':'), 210 | ) 211 | 212 | def __init__(self, data = None, alignment = 0): 213 | Structure.__init__(self,data, alignment) 214 | if data is None: 215 | self['ver_major'] = 5 216 | self['ver_minor'] = 0 217 | self['flags'] = MSRPC_FIRSTFRAG | MSRPC_LASTFRAG 218 | self['type'] = MSRPC_REQUEST 219 | self.__frag_len_set = 0 220 | self['auth_len'] = 0 221 | self['pduData'] = '' 222 | self['auth_data'] = '' 223 | self['sec_trailer'] = '' 224 | self['pad'] = '' 225 | 226 | def get_header_size(self): 227 | return self._SIZE 228 | 229 | def get_packet(self): 230 | if self['auth_data'] != '': 231 | self['auth_len'] = len(self['auth_data']) 232 | # The sec_trailer structure MUST be 4-byte aligned with respect to 233 | # the beginning of the PDU. Padding octets MUST be used to align the 234 | # sec_trailer structure if its natural beginning is not already 4-byte aligned 235 | ##self['pad'] = '\xAA' * (4 - ((self._SIZE + len(self['pduData'])) & 3) & 3) 236 | 237 | return self.getData() 238 | 239 | class MSRPCRequestHeader(MSRPCHeader): 240 | _SIZE = 24 241 | commonHdr = MSRPCHeader.commonHdr + ( 242 | ('alloc_hint',' 0 else 0'), 327 | ('sec_trailer',':'), 328 | ('auth_dataLen','_-auth_data','self["auth_len"]'), 329 | ('auth_data',':'), 330 | ) 331 | def __init__(self, data = None, alignment = 0): 332 | self.__ctx_items = [] 333 | Structure.__init__(self,data,alignment) 334 | if data is None: 335 | self['Pad'] = '' 336 | self['ctx_items'] = '' 337 | self['sec_trailer'] = '' 338 | self['auth_data'] = '' 339 | 340 | def getCtxItems(self): 341 | return self.__ctx_items 342 | 343 | def getCtxItem(self,index): 344 | return self.__ctx_items[index-1] 345 | 346 | def fromString(self, data): 347 | Structure.fromString(self,data) 348 | # Parse the ctx_items 349 | data = self['ctx_items'] 350 | for i in range(self['ctx_num']): 351 | item = CtxItemResult(data) 352 | self.__ctx_items.append(item) 353 | data = data[len(item):] 354 | 355 | class MSRPCBindNak(Structure): 356 | structure = ( 357 | ('RejectedReason',' [big endian] 47 | 48 | usual printf like specifiers can be used (if started with %) 49 | [not recommeneded, there is no why to unpack this] 50 | 51 | %08x will output an 8 bytes hex 52 | %s will output a string 53 | %s\\x00 will output a NUL terminated string 54 | %d%d will output 2 decimal digits (against the very same specification of Structure) 55 | ... 56 | 57 | some additional format specifiers: 58 | : just copy the bytes from the field into the output string (input may be string, other structure, or anything responding to __str__()) (for unpacking, all what's left is returned) 59 | z same as :, but adds a NUL byte at the end (asciiz) (for unpacking the first NUL byte is used as terminator) [asciiz string] 60 | u same as z, but adds two NUL bytes at the end (after padding to an even size with NULs). (same for unpacking) [unicode string] 61 | w DCE-RPC/NDR string (it's a macro for [ ' 2: 147 | dataClassOrCode = field[2] 148 | try: 149 | self[field[0]] = self.unpack(field[1], data[:size], dataClassOrCode = dataClassOrCode, field = field[0]) 150 | except Exception,e: 151 | e.args += ("When unpacking field '%s | %s | %r[:%d]'" % (field[0], field[1], data, size),) 152 | raise 153 | 154 | size = self.calcPackSize(field[1], self[field[0]], field[0]) 155 | if self.alignment and size % self.alignment: 156 | size += self.alignment - (size % self.alignment) 157 | data = data[size:] 158 | 159 | return self 160 | 161 | def __setitem__(self, key, value): 162 | self.fields[key] = value 163 | self.data = None # force recompute 164 | 165 | def __getitem__(self, key): 166 | return self.fields[key] 167 | 168 | def __delitem__(self, key): 169 | del self.fields[key] 170 | 171 | def __str__(self): 172 | return self.getData() 173 | 174 | def __len__(self): 175 | # XXX: improve 176 | return len(self.getData()) 177 | 178 | def pack(self, format, data, field = None): 179 | if self.debug: 180 | print " pack( %s | %r | %s)" % (format, data, field) 181 | 182 | if field: 183 | addressField = self.findAddressFieldFor(field) 184 | if (addressField is not None) and (data is None): 185 | return '' 186 | 187 | # void specifier 188 | if format[:1] == '_': 189 | return '' 190 | 191 | # quote specifier 192 | if format[:1] == "'" or format[:1] == '"': 193 | return format[1:] 194 | 195 | # code specifier 196 | two = format.split('=') 197 | if len(two) >= 2: 198 | try: 199 | return self.pack(two[0], data) 200 | except: 201 | fields = {'self':self} 202 | fields.update(self.fields) 203 | return self.pack(two[0], eval(two[1], {}, fields)) 204 | 205 | # address specifier 206 | two = format.split('&') 207 | if len(two) == 2: 208 | try: 209 | return self.pack(two[0], data) 210 | except: 211 | if (self.fields.has_key(two[1])) and (self[two[1]] is not None): 212 | return self.pack(two[0], id(self[two[1]]) & ((1<<(calcsize(two[0])*8))-1) ) 213 | else: 214 | return self.pack(two[0], 0) 215 | 216 | # length specifier 217 | two = format.split('-') 218 | if len(two) == 2: 219 | try: 220 | return self.pack(two[0],data) 221 | except: 222 | return self.pack(two[0], self.calcPackFieldSize(two[1])) 223 | 224 | # array specifier 225 | two = format.split('*') 226 | if len(two) == 2: 227 | answer = '' 228 | for each in data: 229 | answer += self.pack(two[1], each) 230 | if two[0]: 231 | if two[0].isdigit(): 232 | if int(two[0]) != len(data): 233 | raise Exception, "Array field has a constant size, and it doesn't match the actual value" 234 | else: 235 | return self.pack(two[0], len(data))+answer 236 | return answer 237 | 238 | # "printf" string specifier 239 | if format[:1] == '%': 240 | # format string like specifier 241 | return format % data 242 | 243 | # asciiz specifier 244 | if format[:1] == 'z': 245 | return str(data)+'\0' 246 | 247 | # unicode specifier 248 | if format[:1] == 'u': 249 | return str(data)+'\0\0' + (len(data) & 1 and '\0' or '') 250 | 251 | # DCE-RPC/NDR string specifier 252 | if format[:1] == 'w': 253 | if len(data) == 0: 254 | data = '\0\0' 255 | elif len(data) % 2: 256 | data += '\0' 257 | l = pack('= 2: 304 | return self.unpack(two[0],data) 305 | 306 | # length specifier 307 | two = format.split('-') 308 | if len(two) == 2: 309 | return self.unpack(two[0],data) 310 | 311 | # array specifier 312 | two = format.split('*') 313 | if len(two) == 2: 314 | answer = [] 315 | sofar = 0 316 | if two[0].isdigit(): 317 | number = int(two[0]) 318 | elif two[0]: 319 | sofar += self.calcUnpackSize(two[0], data) 320 | number = self.unpack(two[0], data[:sofar]) 321 | else: 322 | number = -1 323 | 324 | while number and sofar < len(data): 325 | nsofar = sofar + self.calcUnpackSize(two[1],data[sofar:]) 326 | answer.append(self.unpack(two[1], data[sofar:nsofar], dataClassOrCode)) 327 | number -= 1 328 | sofar = nsofar 329 | return answer 330 | 331 | # "printf" string specifier 332 | if format[:1] == '%': 333 | # format string like specifier 334 | return format % data 335 | 336 | # asciiz specifier 337 | if format == 'z': 338 | if data[-1] != '\x00': 339 | raise Exception, ("%s 'z' field is not NUL terminated: %r" % (field, data)) 340 | return data[:-1] # remove trailing NUL 341 | 342 | # unicode specifier 343 | if format == 'u': 344 | if data[-2:] != '\x00\x00': 345 | raise Exception, ("%s 'u' field is not NUL-NUL terminated: %r" % (field, data)) 346 | return data[:-2] # remove trailing NUL 347 | 348 | # DCE-RPC/NDR string specifier 349 | if format == 'w': 350 | l = unpack('= 2: 384 | return self.calcPackSize(two[0], data) 385 | 386 | # length specifier 387 | two = format.split('-') 388 | if len(two) == 2: 389 | return self.calcPackSize(two[0], data) 390 | 391 | # array specifier 392 | two = format.split('*') 393 | if len(two) == 2: 394 | answer = 0 395 | if two[0].isdigit(): 396 | if int(two[0]) != len(data): 397 | raise Exception, "Array field has a constant size, and it doesn't match the actual value" 398 | elif two[0]: 399 | answer += self.calcPackSize(two[0], len(data)) 400 | 401 | for each in data: 402 | answer += self.calcPackSize(two[1], each) 403 | return answer 404 | 405 | # "printf" string specifier 406 | if format[:1] == '%': 407 | # format string like specifier 408 | return len(format % data) 409 | 410 | # asciiz specifier 411 | if format[:1] == 'z': 412 | return len(data)+1 413 | 414 | # asciiz specifier 415 | if format[:1] == 'u': 416 | l = len(data) 417 | return l + (l & 1 and 3 or 2) 418 | 419 | # DCE-RPC/NDR string specifier 420 | if format[:1] == 'w': 421 | l = len(data) 422 | return 12+l+l % 2 423 | 424 | # literal specifier 425 | if format[:1] == ':': 426 | return len(data) 427 | 428 | # struct like specifier 429 | return calcsize(format) 430 | 431 | def calcUnpackSize(self, format, data, field = None): 432 | if self.debug: 433 | print " calcUnpackSize( %s | %s | %r)" % (field, format, data) 434 | 435 | # void specifier 436 | if format[:1] == '_': 437 | return 0 438 | 439 | addressField = self.findAddressFieldFor(field) 440 | if addressField is not None: 441 | if not self[addressField]: 442 | return 0 443 | 444 | try: 445 | lengthField = self.findLengthFieldFor(field) 446 | return self[lengthField] 447 | except: 448 | pass 449 | 450 | # XXX: Try to match to actual values, raise if no match 451 | 452 | # quote specifier 453 | if format[:1] == "'" or format[:1] == '"': 454 | return len(format)-1 455 | 456 | # address specifier 457 | two = format.split('&') 458 | if len(two) == 2: 459 | return self.calcUnpackSize(two[0], data) 460 | 461 | # code specifier 462 | two = format.split('=') 463 | if len(two) >= 2: 464 | return self.calcUnpackSize(two[0], data) 465 | 466 | # length specifier 467 | two = format.split('-') 468 | if len(two) == 2: 469 | return self.calcUnpackSize(two[0], data) 470 | 471 | # array specifier 472 | two = format.split('*') 473 | if len(two) == 2: 474 | answer = 0 475 | if two[0]: 476 | if two[0].isdigit(): 477 | number = int(two[0]) 478 | else: 479 | answer += self.calcUnpackSize(two[0], data) 480 | number = self.unpack(two[0], data[:answer]) 481 | 482 | while number: 483 | number -= 1 484 | answer += self.calcUnpackSize(two[1], data[answer:]) 485 | else: 486 | while answer < len(data): 487 | answer += self.calcUnpackSize(two[1], data[answer:]) 488 | return answer 489 | 490 | # "printf" string specifier 491 | if format[:1] == '%': 492 | raise Exception, "Can't guess the size of a printf like specifier for unpacking" 493 | 494 | # asciiz specifier 495 | if format[:1] == 'z': 496 | return data.index('\x00')+1 497 | 498 | # asciiz specifier 499 | if format[:1] == 'u': 500 | l = data.index('\x00\x00') 501 | return l + (l & 1 and 3 or 2) 502 | 503 | # DCE-RPC/NDR string specifier 504 | if format[:1] == 'w': 505 | l = unpack('L'), 627 | ('code1','>L=len(arr1)*2+0x1000'), 628 | ) 629 | 630 | def populate(self, a): 631 | a['default'] = 'hola' 632 | a['int1'] = 0x3131 633 | a['int3'] = 0x45444342 634 | a['z1'] = 'hola' 635 | a['u1'] = 'hola'.encode('utf_16_le') 636 | a[':1'] = ':1234:' 637 | a['arr1'] = (0x12341234,0x88990077,0x41414141) 638 | # a['len1'] = 0x42424242 639 | 640 | class _Test_fixedLength(_Test_simple): 641 | def populate(self, a): 642 | _Test_simple.populate(self, a) 643 | a['len1'] = 0x42424242 644 | 645 | class _Test_simple_aligned4(_Test_simple): 646 | alignment = 4 647 | 648 | class _Test_nested(_StructureTest): 649 | class theClass(Structure): 650 | class _Inner(Structure): 651 | structure = (('data', 'z'),) 652 | 653 | structure = ( 654 | ('nest1', ':', _Inner), 655 | ('nest2', ':', _Inner), 656 | ('int', '> 8)'), 716 | ('pad', '_','((iv >>2) & 0x3F)'), 717 | ('keyid', '_','( iv & 0x03 )'), 718 | ('dataLen', '_-data', 'len(inputDataLeft)-4'), 719 | ('data',':'), 720 | ('icv','>L'), 721 | ) 722 | 723 | def populate(self, a): 724 | a['init_vector']=0x01020304 725 | #a['pad']=int('01010101',2) 726 | a['pad']=int('010101',2) 727 | a['keyid']=0x07 728 | a['data']="\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9" 729 | a['icv'] = 0x05060708 730 | #a['iv'] = 0x01020304 731 | 732 | if __name__ == '__main__': 733 | _Test_simple().run() 734 | 735 | try: 736 | _Test_fixedLength().run() 737 | except: 738 | print "cannot repack because length is bogus" 739 | 740 | _Test_simple_aligned4().run() 741 | _Test_nested().run() 742 | _Test_Optional().run() 743 | _Test_Optional_sparse().run() 744 | _Test_AsciiZArray().run() 745 | _Test_UnpackCode().run() 746 | _Test_AAA().run() 747 | -------------------------------------------------------------------------------- /aes.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # aes.py: implements AES - Advanced Encryption Standard 4 | # from the SlowAES project, http://code.google.com/p/slowaes/ 5 | # 6 | # Copyright (c) 2008 Josh Davis ( http://www.josh-davis.org ), 7 | # Alex Martelli ( http://www.aleax.it ) 8 | # 9 | # Ported from C code written by Laurent Haan ( http://www.progressive-coding.com ) 10 | # 11 | # Licensed under the Apache License, Version 2.0 12 | # http://www.apache.org/licenses/ 13 | # 14 | import os 15 | import sys 16 | import math 17 | 18 | class AES(object): 19 | '''AES funtions for a single block 20 | ''' 21 | # Very annoying code: all is for an object, but no state is kept! 22 | # Should just be plain functions in a AES modlule. 23 | 24 | v6 = False 25 | 26 | # valid key sizes 27 | keySize = dict(SIZE_128=16, SIZE_192=24, SIZE_256=32) 28 | 29 | # Rijndael S-box 30 | sbox = [0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 31 | 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 32 | 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 33 | 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 34 | 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 35 | 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 36 | 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 37 | 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 38 | 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 39 | 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 40 | 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 41 | 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 42 | 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 43 | 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 44 | 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 45 | 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 46 | 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 47 | 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 48 | 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 49 | 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 50 | 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 51 | 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 52 | 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 53 | 0x54, 0xbb, 0x16] 54 | 55 | # Rijndael Inverted S-box 56 | rsbox = [0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 57 | 0x9e, 0x81, 0xf3, 0xd7, 0xfb , 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 58 | 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb , 0x54, 59 | 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 60 | 0x42, 0xfa, 0xc3, 0x4e , 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 61 | 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25 , 0x72, 0xf8, 62 | 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 63 | 0x65, 0xb6, 0x92 , 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 64 | 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84 , 0x90, 0xd8, 0xab, 65 | 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 66 | 0x45, 0x06 , 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 67 | 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b , 0x3a, 0x91, 0x11, 0x41, 68 | 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 69 | 0x73 , 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 70 | 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e , 0x47, 0xf1, 0x1a, 0x71, 0x1d, 71 | 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b , 72 | 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 73 | 0xfe, 0x78, 0xcd, 0x5a, 0xf4 , 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 74 | 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f , 0x60, 75 | 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 76 | 0x93, 0xc9, 0x9c, 0xef , 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 77 | 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61 , 0x17, 0x2b, 78 | 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 79 | 0x21, 0x0c, 0x7d] 80 | 81 | def getSBoxValue(self,num): 82 | """Retrieves a given S-Box Value""" 83 | return self.sbox[num] 84 | 85 | def getSBoxInvert(self,num): 86 | """Retrieves a given Inverted S-Box Value""" 87 | return self.rsbox[num] 88 | 89 | def rotate(self, word): 90 | """ Rijndael's key schedule rotate operation. 91 | 92 | Rotate a word eight bits to the left: eg, rotate(1d2c3a4f) == 2c3a4f1d 93 | Word is an char list of size 4 (32 bits overall). 94 | """ 95 | return word[1:] + word[:1] 96 | 97 | # Rijndael Rcon 98 | Rcon = [0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 99 | 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 100 | 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 101 | 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 102 | 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 103 | 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 104 | 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 105 | 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 106 | 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 107 | 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 108 | 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 109 | 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 110 | 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 111 | 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 112 | 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 113 | 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 114 | 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 115 | 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 116 | 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 117 | 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 118 | 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 119 | 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 120 | 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 121 | 0xe8, 0xcb ] 122 | 123 | def getRconValue(self, num): 124 | """Retrieves a given Rcon Value""" 125 | return self.Rcon[num] 126 | 127 | def core(self, word, iteration): 128 | """Key schedule core.""" 129 | # rotate the 32-bit word 8 bits to the left 130 | word = self.rotate(word) 131 | # apply S-Box substitution on all 4 parts of the 32-bit word 132 | for i in range(4): 133 | word[i] = self.getSBoxValue(word[i]) 134 | # XOR the output of the rcon operation with i to the first part 135 | # (leftmost) only 136 | word[0] = word[0] ^ self.getRconValue(iteration) 137 | return word 138 | 139 | def expandKey(self, key, size, expandedKeySize): 140 | """Rijndael's key expansion. 141 | 142 | Expands an 128,192,256 key into an 176,208,240 bytes key 143 | 144 | expandedKey is a char list of large enough size, 145 | key is the non-expanded key. 146 | """ 147 | # current expanded keySize, in bytes 148 | currentSize = 0 149 | rconIteration = 1 150 | expandedKey = [0] * expandedKeySize 151 | 152 | # set the 16, 24, 32 bytes of the expanded key to the input key 153 | for j in range(size): 154 | expandedKey[j] = key[j] 155 | currentSize += size 156 | 157 | while currentSize < expandedKeySize: 158 | # assign the previous 4 bytes to the temporary value t 159 | t = expandedKey[currentSize-4:currentSize] 160 | 161 | # every 16,24,32 bytes we apply the core schedule to t 162 | # and increment rconIteration afterwards 163 | if currentSize % size == 0: 164 | t = self.core(t, rconIteration) 165 | rconIteration += 1 166 | # For 256-bit keys, we add an extra sbox to the calculation 167 | if size == self.keySize["SIZE_256"] and ((currentSize % size) == 16): 168 | for l in range(4): t[l] = self.getSBoxValue(t[l]) 169 | 170 | # We XOR t with the four-byte block 16,24,32 bytes before the new 171 | # expanded key. This becomes the next four bytes in the expanded 172 | # key. 173 | for m in range(4): 174 | expandedKey[currentSize] = expandedKey[currentSize - size] ^ \ 175 | t[m] 176 | currentSize += 1 177 | 178 | return expandedKey 179 | 180 | def addRoundKey(self, state, roundKey): 181 | """Adds (XORs) the round key to the state.""" 182 | for i in range(16): 183 | state[i] ^= roundKey[i] 184 | return state 185 | 186 | def createRoundKey(self, expandedKey, roundKeyPointer): 187 | """Create a round key. 188 | Creates a round key from the given expanded key and the 189 | position within the expanded key. 190 | """ 191 | roundKey = [0] * 16 192 | for i in range(4): 193 | for j in range(4): 194 | roundKey[j*4+i] = expandedKey[roundKeyPointer + i*4 + j] 195 | return roundKey 196 | 197 | def galois_multiplication(self, a, b): 198 | """Galois multiplication of 8 bit characters a and b.""" 199 | p = 0 200 | for counter in range(8): 201 | if b & 1: p ^= a 202 | hi_bit_set = a & 0x80 203 | a <<= 1 204 | # keep a 8 bit 205 | a &= 0xFF 206 | if hi_bit_set: 207 | a ^= 0x1b 208 | b >>= 1 209 | return p 210 | 211 | # 212 | # substitute all the values from the state with the value in the SBox 213 | # using the state value as index for the SBox 214 | # 215 | def subBytes(self, state, isInv): 216 | if isInv: getter = self.getSBoxInvert 217 | else: getter = self.getSBoxValue 218 | for i in range(16): state[i] = getter(state[i]) 219 | return state 220 | 221 | # iterate over the 4 rows and call shiftRow() with that row 222 | def shiftRows(self, state, isInv): 223 | for i in range(4): 224 | state = self.shiftRow(state, i*4, i, isInv) 225 | return state 226 | 227 | # each iteration shifts the row to the left by 1 228 | def shiftRow(self, state, statePointer, nbr, isInv): 229 | for i in range(nbr): 230 | if isInv: 231 | state[statePointer:statePointer+4] = \ 232 | state[statePointer+3:statePointer+4] + \ 233 | state[statePointer:statePointer+3] 234 | else: 235 | state[statePointer:statePointer+4] = \ 236 | state[statePointer+1:statePointer+4] + \ 237 | state[statePointer:statePointer+1] 238 | return state 239 | 240 | # galois multiplication of the 4x4 matrix 241 | def mixColumns(self, state, isInv): 242 | # iterate over the 4 columns 243 | for i in range(4): 244 | # construct one column by slicing over the 4 rows 245 | column = state[i:i+16:4] 246 | # apply the mixColumn on one column 247 | column = self.mixColumn(column, isInv) 248 | # put the values back into the state 249 | state[i:i+16:4] = column 250 | 251 | return state 252 | 253 | # galois multiplication of 1 column of the 4x4 matrix 254 | def mixColumn(self, column, isInv): 255 | if isInv: mult = [14, 9, 13, 11] 256 | else: mult = [2, 1, 1, 3] 257 | cpy = list(column) 258 | g = self.galois_multiplication 259 | 260 | column[0] = g(cpy[0], mult[0]) ^ g(cpy[3], mult[1]) ^ \ 261 | g(cpy[2], mult[2]) ^ g(cpy[1], mult[3]) 262 | column[1] = g(cpy[1], mult[0]) ^ g(cpy[0], mult[1]) ^ \ 263 | g(cpy[3], mult[2]) ^ g(cpy[2], mult[3]) 264 | column[2] = g(cpy[2], mult[0]) ^ g(cpy[1], mult[1]) ^ \ 265 | g(cpy[0], mult[2]) ^ g(cpy[3], mult[3]) 266 | column[3] = g(cpy[3], mult[0]) ^ g(cpy[2], mult[1]) ^ \ 267 | g(cpy[1], mult[2]) ^ g(cpy[0], mult[3]) 268 | return column 269 | 270 | # applies the 4 operations of the forward round in sequence 271 | def aes_round(self, state, roundKey, round): 272 | state = self.subBytes(state, False) 273 | state = self.shiftRows(state, False) 274 | state = self.mixColumns(state, False) 275 | 276 | if self.v6: 277 | if round == 4: 278 | state[0]^=0x73 279 | if round == 6: 280 | state[0]^=0x09 281 | if round == 8: 282 | state[0]^=0xE4 283 | 284 | state = self.addRoundKey(state, roundKey) 285 | return state 286 | 287 | # applies the 4 operations of the inverse round in sequence 288 | def aes_invRound(self, state, roundKey, round): 289 | state = self.shiftRows(state, True) 290 | state = self.subBytes(state, True) 291 | state = self.addRoundKey(state, roundKey) 292 | 293 | if self.v6: 294 | if round == 4: 295 | state[0]^=0x73 296 | if round == 6: 297 | state[0]^=0x09 298 | if round == 8: 299 | state[0]^=0xE4 300 | 301 | state = self.mixColumns(state, True) 302 | return state 303 | 304 | # Perform the initial operations, the standard round, and the final 305 | # operations of the forward aes, creating a round key for each round 306 | def aes_main(self, state, expandedKey, nbrRounds): 307 | state = self.addRoundKey(state, self.createRoundKey(expandedKey, 0)) 308 | i = 1 309 | while i < nbrRounds: 310 | state = self.aes_round(state, 311 | self.createRoundKey(expandedKey, 16*i), i) 312 | i += 1 313 | state = self.subBytes(state, False) 314 | state = self.shiftRows(state, False) 315 | state = self.addRoundKey(state, 316 | self.createRoundKey(expandedKey, 16*nbrRounds)) 317 | return state 318 | 319 | # Perform the initial operations, the standard round, and the final 320 | # operations of the inverse aes, creating a round key for each round 321 | def aes_invMain(self, state, expandedKey, nbrRounds): 322 | state = self.addRoundKey(state, 323 | self.createRoundKey(expandedKey, 16*nbrRounds)) 324 | i = nbrRounds - 1 325 | while i > 0: 326 | state = self.aes_invRound(state, 327 | self.createRoundKey(expandedKey, 16*i), i) 328 | i -= 1 329 | state = self.shiftRows(state, True) 330 | state = self.subBytes(state, True) 331 | state = self.addRoundKey(state, self.createRoundKey(expandedKey, 0)) 332 | return state 333 | 334 | # encrypts a 128 bit input block against the given key of size specified 335 | def encrypt(self, iput, key, size): 336 | output = [0] * 16 337 | # the number of rounds 338 | nbrRounds = 0 339 | # the 128 bit block to encode 340 | block = [0] * 16 341 | # set the number of rounds 342 | if size == self.keySize["SIZE_128"]: nbrRounds = 10 343 | elif size == self.keySize["SIZE_192"]: nbrRounds = 12 344 | elif size == self.keySize["SIZE_256"]: nbrRounds = 14 345 | else: return None 346 | 347 | # the expanded keySize 348 | expandedKeySize = 16*(nbrRounds+1) 349 | 350 | # Set the block values, for the block: 351 | # a0,0 a0,1 a0,2 a0,3 352 | # a1,0 a1,1 a1,2 a1,3 353 | # a2,0 a2,1 a2,2 a2,3 354 | # a3,0 a3,1 a3,2 a3,3 355 | # the mapping order is a0,0 a1,0 a2,0 a3,0 a0,1 a1,1 ... a2,3 a3,3 356 | # 357 | # iterate over the columns 358 | for i in range(4): 359 | # iterate over the rows 360 | for j in range(4): 361 | block[(i+(j*4))] = iput[(i*4)+j] 362 | 363 | # expand the key into an 176, 208, 240 bytes key 364 | # the expanded key 365 | expandedKey = self.expandKey(key, size, expandedKeySize) 366 | 367 | # encrypt the block using the expandedKey 368 | block = self.aes_main(block, expandedKey, nbrRounds) 369 | 370 | # unmap the block again into the output 371 | for k in range(4): 372 | # iterate over the rows 373 | for l in range(4): 374 | output[(k*4)+l] = block[(k+(l*4))] 375 | return output 376 | 377 | # decrypts a 128 bit input block against the given key of size specified 378 | def decrypt(self, iput, key, size): 379 | output = [0] * 16 380 | # the number of rounds 381 | nbrRounds = 0 382 | # the 128 bit block to decode 383 | block = [0] * 16 384 | # set the number of rounds 385 | if size == self.keySize["SIZE_128"]: nbrRounds = 10 386 | elif size == self.keySize["SIZE_192"]: nbrRounds = 12 387 | elif size == self.keySize["SIZE_256"]: nbrRounds = 14 388 | else: return None 389 | 390 | # the expanded keySize 391 | expandedKeySize = 16*(nbrRounds+1) 392 | 393 | # Set the block values, for the block: 394 | # a0,0 a0,1 a0,2 a0,3 395 | # a1,0 a1,1 a1,2 a1,3 396 | # a2,0 a2,1 a2,2 a2,3 397 | # a3,0 a3,1 a3,2 a3,3 398 | # the mapping order is a0,0 a1,0 a2,0 a3,0 a0,1 a1,1 ... a2,3 a3,3 399 | 400 | # iterate over the columns 401 | for i in range(4): 402 | # iterate over the rows 403 | for j in range(4): 404 | block[(i+(j*4))] = iput[(i*4)+j] 405 | # expand the key into an 176, 208, 240 bytes key 406 | expandedKey = self.expandKey(key, size, expandedKeySize) 407 | # decrypt the block using the expandedKey 408 | block = self.aes_invMain(block, expandedKey, nbrRounds) 409 | # unmap the block again into the output 410 | for k in range(4): 411 | # iterate over the rows 412 | for l in range(4): 413 | output[(k*4)+l] = block[(k+(l*4))] 414 | return output 415 | 416 | 417 | class AESModeOfOperation(object): 418 | '''Handles AES with plaintext consistingof multiple blocks. 419 | Choice of block encoding modes: OFT, CFB, CBC 420 | ''' 421 | # Very annoying code: all is for an object, but no state is kept! 422 | # Should just be plain functions in an AES_BlockMode module. 423 | aes = AES() 424 | 425 | # structure of supported modes of operation 426 | modeOfOperation = dict(OFB=0, CFB=1, CBC=2) 427 | 428 | # converts a 16 character string into a number array 429 | def convertString(self, string, start, end, mode): 430 | if end - start > 16: end = start + 16 431 | if mode == self.modeOfOperation["CBC"]: ar = [0] * 16 432 | else: ar = [] 433 | 434 | i = start 435 | j = 0 436 | while len(ar) < end - start: 437 | ar.append(0) 438 | while i < end: 439 | ar[j] = ord(string[i]) 440 | j += 1 441 | i += 1 442 | return ar 443 | 444 | # Mode of Operation Encryption 445 | # stringIn - Input String 446 | # mode - mode of type modeOfOperation 447 | # hexKey - a hex key of the bit length size 448 | # size - the bit length of the key 449 | # hexIV - the 128 bit hex Initilization Vector 450 | def encrypt(self, stringIn, mode, key, size, IV): 451 | if len(key) % size: 452 | return None 453 | if len(IV) % 16: 454 | return None 455 | # the AES input/output 456 | plaintext = [] 457 | iput = [0] * 16 458 | output = [] 459 | ciphertext = [0] * 16 460 | # the output cipher string 461 | cipherOut = [] 462 | # char firstRound 463 | firstRound = True 464 | if stringIn != None: 465 | for j in range(int(math.ceil(float(len(stringIn))/16))): 466 | start = j*16 467 | end = j*16+16 468 | if end > len(stringIn): 469 | end = len(stringIn) 470 | plaintext = self.convertString(stringIn, start, end, mode) 471 | # print 'PT@%s:%s' % (j, plaintext) 472 | if mode == self.modeOfOperation["CFB"]: 473 | if firstRound: 474 | output = self.aes.encrypt(IV, key, size) 475 | firstRound = False 476 | else: 477 | output = self.aes.encrypt(iput, key, size) 478 | for i in range(16): 479 | if len(plaintext)-1 < i: 480 | ciphertext[i] = 0 ^ output[i] 481 | elif len(output)-1 < i: 482 | ciphertext[i] = plaintext[i] ^ 0 483 | elif len(plaintext)-1 < i and len(output) < i: 484 | ciphertext[i] = 0 ^ 0 485 | else: 486 | ciphertext[i] = plaintext[i] ^ output[i] 487 | for k in range(end-start): 488 | cipherOut.append(ciphertext[k]) 489 | iput = ciphertext 490 | elif mode == self.modeOfOperation["OFB"]: 491 | if firstRound: 492 | output = self.aes.encrypt(IV, key, size) 493 | firstRound = False 494 | else: 495 | output = self.aes.encrypt(iput, key, size) 496 | for i in range(16): 497 | if len(plaintext)-1 < i: 498 | ciphertext[i] = 0 ^ output[i] 499 | elif len(output)-1 < i: 500 | ciphertext[i] = plaintext[i] ^ 0 501 | elif len(plaintext)-1 < i and len(output) < i: 502 | ciphertext[i] = 0 ^ 0 503 | else: 504 | ciphertext[i] = plaintext[i] ^ output[i] 505 | for k in range(end-start): 506 | cipherOut.append(ciphertext[k]) 507 | iput = output 508 | elif mode == self.modeOfOperation["CBC"]: 509 | for i in range(16): 510 | if firstRound: 511 | iput[i] = plaintext[i] ^ IV[i] 512 | else: 513 | iput[i] = plaintext[i] ^ ciphertext[i] 514 | # print 'IP@%s:%s' % (j, iput) 515 | firstRound = False 516 | ciphertext = self.aes.encrypt(iput, key, size) 517 | # always 16 bytes because of the padding for CBC 518 | for k in range(16): 519 | cipherOut.append(ciphertext[k]) 520 | return mode, len(stringIn), cipherOut 521 | 522 | # Mode of Operation Decryption 523 | # cipherIn - Encrypted String 524 | # originalsize - The unencrypted string length - required for CBC 525 | # mode - mode of type modeOfOperation 526 | # key - a number array of the bit length size 527 | # size - the bit length of the key 528 | # IV - the 128 bit number array Initilization Vector 529 | def decrypt(self, cipherIn, originalsize, mode, key, size, IV): 530 | # cipherIn = unescCtrlChars(cipherIn) 531 | if len(key) % size: 532 | return None 533 | if len(IV) % 16: 534 | return None 535 | # the AES input/output 536 | ciphertext = [] 537 | iput = [] 538 | output = [] 539 | plaintext = [0] * 16 540 | # the output plain text character list 541 | chrOut = [] 542 | # char firstRound 543 | firstRound = True 544 | if cipherIn != None: 545 | for j in range(int(math.ceil(float(len(cipherIn))/16))): 546 | start = j*16 547 | end = j*16+16 548 | if j*16+16 > len(cipherIn): 549 | end = len(cipherIn) 550 | ciphertext = cipherIn[start:end] 551 | if mode == self.modeOfOperation["CFB"]: 552 | if firstRound: 553 | output = self.aes.encrypt(IV, key, size) 554 | firstRound = False 555 | else: 556 | output = self.aes.encrypt(iput, key, size) 557 | for i in range(16): 558 | if len(output)-1 < i: 559 | plaintext[i] = 0 ^ ciphertext[i] 560 | elif len(ciphertext)-1 < i: 561 | plaintext[i] = output[i] ^ 0 562 | elif len(output)-1 < i and len(ciphertext) < i: 563 | plaintext[i] = 0 ^ 0 564 | else: 565 | plaintext[i] = output[i] ^ ciphertext[i] 566 | for k in range(end-start): 567 | chrOut.append(chr(plaintext[k])) 568 | iput = ciphertext 569 | elif mode == self.modeOfOperation["OFB"]: 570 | if firstRound: 571 | output = self.aes.encrypt(IV, key, size) 572 | firstRound = False 573 | else: 574 | output = self.aes.encrypt(iput, key, size) 575 | for i in range(16): 576 | if len(output)-1 < i: 577 | plaintext[i] = 0 ^ ciphertext[i] 578 | elif len(ciphertext)-1 < i: 579 | plaintext[i] = output[i] ^ 0 580 | elif len(output)-1 < i and len(ciphertext) < i: 581 | plaintext[i] = 0 ^ 0 582 | else: 583 | plaintext[i] = output[i] ^ ciphertext[i] 584 | for k in range(end-start): 585 | chrOut.append(chr(plaintext[k])) 586 | iput = output 587 | elif mode == self.modeOfOperation["CBC"]: 588 | output = self.aes.decrypt(ciphertext, key, size) 589 | for i in range(16): 590 | if firstRound: 591 | plaintext[i] = IV[i] ^ output[i] 592 | else: 593 | plaintext[i] = iput[i] ^ output[i] 594 | firstRound = False 595 | if originalsize is not None and originalsize < end: 596 | for k in range(originalsize-start): 597 | chrOut.append(chr(plaintext[k])) 598 | else: 599 | for k in range(end-start): 600 | chrOut.append(chr(plaintext[k])) 601 | iput = ciphertext 602 | return "".join(chrOut) 603 | 604 | 605 | def append_PKCS7_padding(s): 606 | """return s padded to a multiple of 16-bytes by PKCS7 padding""" 607 | numpads = 16 - (len(s)%16) 608 | return s + numpads*chr(numpads) 609 | 610 | def strip_PKCS7_padding(s): 611 | """return s stripped of PKCS7 padding""" 612 | if len(s)%16 or not s: 613 | raise ValueError("String of len %d can't be PCKS7-padded" % len(s)) 614 | numpads = ord(s[-1]) 615 | if numpads > 16: 616 | return s 617 | # raise ValueError("String ending with %r can't be PCKS7-padded" % s[-1]) 618 | return s[:-numpads] 619 | 620 | def encryptData(key, data, mode=AESModeOfOperation.modeOfOperation["CBC"]): 621 | """encrypt `data` using `key` 622 | 623 | `key` should be a string of bytes. 624 | 625 | returned cipher is a string of bytes prepended with the initialization 626 | vector. 627 | 628 | """ 629 | key = map(ord, key) 630 | if mode == AESModeOfOperation.modeOfOperation["CBC"]: 631 | data = append_PKCS7_padding(data) 632 | keysize = len(key) 633 | assert keysize in AES.keySize.values(), 'invalid key size: %s' % keysize 634 | # create a new iv using random data 635 | iv = [ord(i) for i in os.urandom(16)] 636 | moo = AESModeOfOperation() 637 | (mode, length, ciph) = moo.encrypt(data, mode, key, keysize, iv) 638 | # With padding, the original length does not need to be known. It's a bad 639 | # idea to store the original message length. 640 | # prepend the iv. 641 | return ''.join(map(chr, iv)) + ''.join(map(chr, ciph)) 642 | 643 | def decryptData(key, data, mode=AESModeOfOperation.modeOfOperation["CBC"]): 644 | """decrypt `data` using `key` 645 | 646 | `key` should be a string of bytes. 647 | 648 | `data` should have the initialization vector prepended as a string of 649 | ordinal values. 650 | """ 651 | 652 | key = map(ord, key) 653 | keysize = len(key) 654 | assert keysize in AES.keySize.values(), 'invalid key size: %s' % keysize 655 | # iv is first 16 bytes 656 | iv = map(ord, data[:16]) 657 | data = map(ord, data[16:]) 658 | moo = AESModeOfOperation() 659 | decr = moo.decrypt(data, None, mode, key, keysize, iv) 660 | if mode == AESModeOfOperation.modeOfOperation["CBC"]: 661 | decr = strip_PKCS7_padding(decr) 662 | return decr 663 | 664 | def generateRandomKey(keysize): 665 | """Generates a key from random data of length `keysize`. 666 | The returned key is a string of bytes. 667 | """ 668 | if keysize not in (16, 24, 32): 669 | emsg = 'Invalid keysize, %s. Should be one of (16, 24, 32).' 670 | raise ValueError, emsg % keysize 671 | return os.urandom(keysize) 672 | 673 | def testStr(cleartext, keysize=16, modeName = "CBC"): 674 | '''Test with random key, choice of mode.''' 675 | print 'Random key test', 'Mode:', modeName 676 | print 'cleartext:', cleartext 677 | key = generateRandomKey(keysize) 678 | print 'Key:', [ord(x) for x in key] 679 | mode = AESModeOfOperation.modeOfOperation[modeName] 680 | cipher = encryptData(key, cleartext, mode) 681 | print 'Cipher:', [ord(x) for x in cipher] 682 | decr = decryptData(key, cipher, mode) 683 | print 'Decrypted:', decr 684 | 685 | 686 | if __name__ == "__main__": 687 | moo = AESModeOfOperation() 688 | cleartext = "This is a test with several blocks!" 689 | cypherkey = [143,194,34,208,145,203,230,143,177,246,97,206,145,92,255,84] 690 | iv = [103,35,148,239,76,213,47,118,255,222,123,176,106,134,98,92] 691 | mode, orig_len, ciph = moo.encrypt(cleartext, moo.modeOfOperation["CBC"], 692 | cypherkey, moo.aes.keySize["SIZE_128"], iv) 693 | print 'm=%s, ol=%s (%s), ciph=%s' % (mode, orig_len, len(cleartext), ciph) 694 | decr = moo.decrypt(ciph, orig_len, mode, cypherkey, 695 | moo.aes.keySize["SIZE_128"], iv) 696 | print decr 697 | testStr(cleartext, 16, "CBC") 698 | --------------------------------------------------------------------------------