samedi 27 juin 2015

AES decryption in python with PyCrypto

I'm trying to write an encryption/decryption program for Python, using PyCrypto The way the program works is that you specify an input/output, choose your input mode (for the sake of this question AES), and the program encrypts the input to the output file while also writing the encryption key to a file called encryption.key. To decrypt, you specify an input/output again, choose your mode (AES) again, and then the program loads the key from the encryption.key and uses it to decrypt the input.

The problem is, I'm a novice at python and cryptography and don't know how to fix the problem I'm getting. The problem is this: encryption works fine (as far as I can tell), but on decryption binascii throws an error about the keyfile having invalid padding. I'm not sure how to pad correctly- I tried implementing some code to pad it (based off the message pad) but it still throws the error. Does anyone know how to pad this key correctly?

Forgive me for being such a noob, everything I know about AES encryption came from me reverse-engineering some code I found in the source of Google's Keyczar's python implementation.

Now that I think about it, I don't really know why I just didn't use keyczar...

CODE:

#!/usr/bin/python
# -*- coding: utf-8 -*-
# P-Cypher-Dev
# Made in 2015 by Mateo Guynn
# v0.05 dev
# Using AES encryption \ Caesar Cipher
# DEV VERSION: Possibly unstable, contains better code.
# Changelog:
"""
v0.02d
- Improved Caesar Cipher
- Added binary Cipher converter (fail)
-------------FILE BROKEN------------
"""
"""
v0.03d
- Added ability to create new output files
- Fixed code not quitting on abort
- DEL : binary Cipher converter 
---------------STABLE---------------

"""
"""
v0.04d
- DEL : Caesar Cypher
- Added 16/32/64-byte AES encryption
- Binary and text now handled in same manner
-------------FILE BROKEN------------
(encryption works, decryption does not)

"""
"""
v0.05d
- Changed AES encryption to Google's way
- Fixed Key entry
- Added mode switcher 
- Readded Caesar Cipher to mode switcher (defaults to AES)
----------------O.K.----------------
       (AES decryption broken)

"""
"""
v0.05g (v0.05 gui)
- Initial tests of a GUI


"""
import os
import sys#!/usr/bin/python
# -*- coding: utf-8 -*-
# P-Cypher-Dev
# Made in 2015 by Mateo Guynn
# v0.05 dev
# Using AES encryption \ Caesar Cipher
# DEV VERSION: Possibly unstable, contains better code.
# Changelog:
"""
v0.02d
- Improved Caesar Cipher
- Added binary Cipher converter (fail)
-------------FILE BROKEN------------
"""
"""
v0.03d
- Added ability to create new output files
- Fixed code not quitting on abort
- DEL : binary Cipher converter 
---------------STABLE---------------

"""
"""
v0.04d
- DEL : Caesar Cypher
- Added 16/32/64-byte AES encryption
- Binary and text now handled in same manner
-------------FILE BROKEN------------
(encryption works, decryption does not)

"""
"""
v0.05d
- Changed AES encryption to Google's way
- Fixed Key entry
- Added mode switcher 
- Readded Caesar Cipher to mode switcher (defaults to AES)
----------------O.K.----------------
       (AES decryption broken)

"""
"""
v0.05g (v0.05 gui)
- Initial tests of a GUI


"""
import os
import sys
from Crypto.Cipher import AES
from Crypto import Random
from Crypto.Util import randpool
import base64

currentEncryptions = ['AES', 'Caesar Cipher']

def getMode():

    while True:

        eOrD = input('\nDo you wish to encrypt or decrypt a message? ')

        mode = eOrD.lower()

        if mode in 'encrypt e decrypt d'.split():

            return mode

        else:

            sys.exit('\nEnter either "encrypt" or "e" or "decrypt" or "d". Capital letters are allowed.\n')



def getMessage():


    inputFile = open(input('\nPlease enter the name of the file you want to encrypt/decrypt. You may use relative or full paths. \nPlease, remember the file extension(s)! ')).read()
    try:
        print ('\nThe contents of the file are: \n%s\n' % inputFile)
        return inputFile
    except IOError as e:
        sys.exit('Unable to open file (the file does not exist or P-Cypher does not have permission to view it).\n Aborting.')
    except FileNotFoundError as e:
        sys.exit('Unable to open file (the file does not exist or P-Cypher does not have permission to view it).\n Aborting.')


def getAESCipher(mode, message):
    block_size = 16 # For AES, this is the only working value
    key_size = 32 # Size of crypto key (possibly changes in getKey())
    aesmode = AES.MODE_CBC # More secure mode
    if mode[0] == 'e':
        key_bytes = randpool.RandomPool(512).get_bytes(key_size)
        pad_key = block_size - len(key_bytes) % block_size
        key_bytes_write = key_bytes + pad_key * bytearray(pad_key)
        open('decryption.key', 'wb+').write(key_bytes_write)
        print('\nYour keyfile is: decryption.key\n')
        pad = block_size - len(message) % block_size
        data = message + pad * chr(pad)
        iv_bytes = randpool.RandomPool(512).get_bytes(block_size)
        encrypted_bytes = iv_bytes + AES.new(key_bytes,aesmode,iv_bytes).encrypt(data)
        encrypted = base64.urlsafe_b64encode(encrypted_bytes)
        return encrypted
    else: 
        decryptb = base64.urlsafe_b64decode(message)
        decrypted_ivbytes = decryptb[:block_size]
        decrypt = decryptb[block_size:]
        print('\nAuto-searching for decryption.key...')
        try:
            key_bytes = base64.urlsafe_b64decode(open('decryption.key', 'rb').read())
        except IOError as io:
            key_bytes = base64.urlsafe_b64decode(open(input('decryption.key not found. If you have an alternate keyfile, please enter its name now. ')), 'rb').read
        except FileNotFoundError as fnf:
            key_bytes = base64.urlsafe_b64decode(open(input('decryption.key not found. If you have an alternate keyfile, please enter its name now. '), 'rb').read())
        decrypted = AES.new(key_bytes, aesmode, decrypted_ivbytes).decrypt(decryptb)
        pad = ord(decrypted[-1])
        decrypted = decrypted[:-pad]
        return decrypted

def getOutput():


    outputFile = input('\nPlease specify an output file. \nDon\'t forget the file extension! ')

    outputCheck = input('\nYour message will be encrypted/decrypted into the following output file: %s\n\nIs this okay? (y/n) ' % outputFile).lower()


    if outputCheck in 'y yes yeah ok'.split():

        try:
            if os.stat(outputFile).st_size != 0:
                overwriteFile = input('The file you specified is not empty. Do you want to overwrite it? (y/n)')
                if overwriteFile in 'y_yes_yeah_yes please_please_ok'.split('_'):
                    return outputFile
                else:
                    sys.exit('Aborting.') 
            else:
                return outputFile
        except IOError as ioerror:
            createNewFile = input('The file you specified does not exist. Shall I create one? (y/n) ').lower()
            if createNewFile in 'y_yes_yeah_yes please_ok_please'.split('_'):
                oF = open(outputFile, 'w+')
                oF.close()
                return outputFile

            else:
                sys.exit('Aborting...')
    elif outputCheck in 'n no'.split():
        sys.exit('\nAborting...\n')
    else:
        sys.exit('\nAborting.\n')

def getEncrypt(mode,message,cipherMode):
    case = cipherMode

    if case == 0: # If the cipherMode is set to AES -- implemented

        return getAESCipher(mode,message)


    elif case == 1: # If the cipherMode is set to Blowfish -- not implemented

        return getBlowfishCipher(mode,message)


    elif case == 2: # If the cipherMode is set to SHA -- not implemented

        return getSHAHash(mode,message)


    elif case == 3: # If the cipherMode is set to HMAC -- not implemented

        return getHMACHash(mode,message)


    elif case == 4: # If the cipherMode is set to PublicKey -- not implemented

        return getPublicKey(mode,message)


    elif case == 5: # If the cipherMode is set to Caesar -- implemented

        return getCaesarCipher(mode,message)


    else: # If no case is found

        print('No cipher mode found. Defaulting to AES.')
        return getAESCipher(mode,message)



def getShiftNum(mode): # Used in the Caesar Cipher to determine the amount of characters to shift the message.

    if mode[0] == 'e':
        shiftNumInput = input('\nHow many characters would you like to shift the message? (1-26) ')
    else:
        shiftNumInput = input('\nPlease enter your shift number. (1-26) ')
    try:
        shiftNum = int(shiftNumInput)

        if shiftNum >= 1 and shiftNum <= 26:

            return shiftNum

        else:

            sys.exit('\nSorry, but that\'s not in the specified range. Aborting.\n')

    except TypeError:

        sys.exit('\nSorry, but that\'s not an integer. Aborting.\n')

    except:

        sys.exit('Input error. Aborting.')

def getBlowfishCipher(mode,message):

    # This ain't implemented yet! Coming soon (possibly)
    sys.exit('Sorry, but blowfish encryption is not currently implemented. Available encryptions: %s' % currentEncryptions)

def getSHAHash(mode,message):

    # This ain't implemented yet! Coming soon (possibly)
    sys.exit('Sorry, but SHA hash encryption is not currently implemented. Available encryptions: %s' % currentEncryptions)

def getHMACHash(mode,message):

    # This ain't implemented yet! Coming soon (possibly)
    sys.exit('Sorry, but HMAC hash encryption is not currently implemented. Available encryptions: %s' % currentEncryptions)

def getPublicKey(mode,message):

    # This ain't implemented yet! Coming soon (possibly)
    sys.exit('Sorry, but public keys are not currently implemented. Available encryptions: %s' % currentEncryptions)

def getCaesarCipher(mode,message):

    key = getShiftNum(mode)

    encryptedMessage = ''

    if mode[0] == 'd':

        key = -key

    for symbol in message:

        if symbol.isalpha():

            num = ord(symbol)
            num += key

            if symbol.isupper():
                if num > ord('Z'):
                    num -= 26
                elif num < ord('A'):
                    num += 26
            elif symbol.islower():
                if num > ord('z'):
                    num -= 26
                elif num < ord('a'):
                    num += 26

            encryptedMessage += chr(num)
        else:
            encryptedMessage += symbol
    return encryptedMessage

def getCipherMode():
    cipherModeInput = input('\nPlease select your encryption mode. Available encryptions are: %s. ' % currentEncryptions).lower()

    if cipherModeInput in 'aes_advanced encryption standard_a'.split('_'):

        print('\nEncrypting with AES.')
        cipherMode = 0
        return cipherMode

    elif cipherModeInput in 'blowfish b blow fish'.split():

        print('\nEncrypting with Blowfish.')
        cipherMode = 1
        return cipherMode

    elif cipherModeInput in 'sha_sha-256_sha-512_sha-1_sha-2_sha-3_secure hash algorithm_secure hash_sha 256_ sha 512_sha 1_sha 2_sha 3'.split('_'):

        print('\nEncrypting with SHA.')
        cipherMode = 2
        return cipherMode

    elif cipherModeInput in 'hmac_hash-based message authentication code_hash based message authentication code_h'.split('_'):

        print('\nEncrypting with HMAC.')
        cipherMode = 3
        return cipherMode

    elif cipherModeInput in 'p_pk_k_public_key_public key'.split('_'):

        print('\nEncrypting with a public key.')
        cipherMode = 4
        return cipherMode

    elif cipherModeInput in 'caesar_cipher_cypher_caesar cipher_caesar cypher_shifter'.split('_'):

        print('\nEncrypting with a Caesar Cipher.')
        cipherMode = 5
        return cipherMode

    else:

        cipherMode = 20
        return cipherMode

def startup():
    print("\nP-Cypher Alpha starting up...\n\nv0.05 dev\nMateo Guynn\n2015\n")
    mode = getMode()

    message = getMessage()

    cipherMode = getCipherMode()
    if cipherMode != 5:
        try:
            open(getOutput(), 'wb').write(getEncrypt(mode,message,cipherMode))
        except IOError:
            sys.exit('Oh noes! Something has gone terribly wrong!')
        except FileNotFoundError:
            sys.exit('Your input file was not found.')
    else:
        try:
            open(getOutput(), 'w+').write(getEncrypt(mode,message,cipherMode))
        except IOError:
            sys.exit('Oh noes! Something has gone terribly wrong!')
        except FileNotFoundError:
            sys.exit('Your input file was not found.')

    print('\nDone.')

startup()
from Crypto.Cipher import AES
from Crypto import Random
from Crypto.Util import randpool
import base64

currentEncryptions = ['AES', 'Caesar Cipher']

def getMode():

    while True:

        eOrD = input('\nDo you wish to encrypt or decrypt a message? ')

        mode = eOrD.lower()

        if mode in 'encrypt e decrypt d'.split():

            return mode

        else:

            sys.exit('\nEnter either "encrypt" or "e" or "decrypt" or "d". Capital letters are allowed.\n')



def getMessage():


    inputFile = open(input('\nPlease enter the name of the file you want to encrypt/decrypt. You may use relative or full paths. \nPlease, remember the file extension(s)! ')).read()
    try:
        print ('\nThe contents of the file are: \n%s\n' % inputFile)
        return inputFile
    except IOError as e:
        sys.exit('Unable to open file (the file does not exist or P-Cypher does not have permission to view it).\n Aborting.')
    except FileNotFoundError as e:
        sys.exit('Unable to open file (the file does not exist or P-Cypher does not have permission to view it).\n Aborting.')


def getAESCipher(mode, message):
    block_size = 16 # For AES, this is the only working value
    key_size = 32 # Size of crypto key (possibly changes in getKey())
    aesmode = AES.MODE_CBC # More secure mode
    if mode[0] == 'e':
        key_bytes = randpool.RandomPool(512).get_bytes(key_size)
        pad_key = block_size - len(key_bytes) % block_size
        key_bytes_write = key_bytes + pad_key * bytearray(pad_key)
        open('decryption.key', 'wb+').write(key_bytes_write)
        print('\nYour keyfile is: decryption.key\n')
        pad = block_size - len(message) % block_size
        data = message + pad * chr(pad)
        iv_bytes = randpool.RandomPool(512).get_bytes(block_size)
        encrypted_bytes = iv_bytes + AES.new(key_bytes,aesmode,iv_bytes).encrypt(data)
        encrypted = base64.urlsafe_b64encode(encrypted_bytes)
        return encrypted
    else: 
        decryptb = base64.urlsafe_b64decode(message)
        decrypted_ivbytes = decryptb[:block_size]
        decrypt = decryptb[block_size:]
        print('\nAuto-searching for decryption.key...')
        try:
            key_bytes = base64.urlsafe_b64decode(open('decryption.key', 'rb').read())
        except IOError as io:
            key_bytes = base64.urlsafe_b64decode(open(input('decryption.key not found. If you have an alternate keyfile, please enter its name now. ')), 'rb').read
        except FileNotFoundError as fnf:
            key_bytes = base64.urlsafe_b64decode(open(input('decryption.key not found. If you have an alternate keyfile, please enter its name now. '), 'rb').read())
        decrypted = AES.new(key_bytes, aesmode, decrypted_ivbytes).decrypt(decryptb)
        pad = ord(decrypted[-1])
        decrypted = decrypted[:-pad]
        return decrypted

def getOutput():


    outputFile = input('\nPlease specify an output file. \nDon\'t forget the file extension! ')

    outputCheck = input('\nYour message will be encrypted/decrypted into the following output file: %s\n\nIs this okay? (y/n) ' % outputFile).lower()


    if outputCheck in 'y yes yeah ok'.split():

        try:
            if os.stat(outputFile).st_size != 0:
                overwriteFile = input('The file you specified is not empty. Do you want to overwrite it? (y/n)')
                if overwriteFile in 'y_yes_yeah_yes please_please_ok'.split('_'):
                    return outputFile
                else:
                    sys.exit('Aborting.') 
            else:
                return outputFile
        except IOError as ioerror:
            createNewFile = input('The file you specified does not exist. Shall I create one? (y/n) ').lower()
            if createNewFile in 'y_yes_yeah_yes please_ok_please'.split('_'):
                oF = open(outputFile, 'w+')
                oF.close()
                return outputFile

            else:
                sys.exit('Aborting...')
    elif outputCheck in 'n no'.split():
        sys.exit('\nAborting...\n')
    else:
        sys.exit('\nAborting.\n')

def getEncrypt(mode,message,cipherMode):
    case = cipherMode

    if case == 0: # If the cipherMode is set to AES -- implemented

        return getAESCipher(mode,message)


    elif case == 1: # If the cipherMode is set to Blowfish -- not implemented

        return getBlowfishCipher(mode,message)


    elif case == 2: # If the cipherMode is set to SHA -- not implemented

        return getSHAHash(mode,message)


    elif case == 3: # If the cipherMode is set to HMAC -- not implemented

        return getHMACHash(mode,message)


    elif case == 4: # If the cipherMode is set to PublicKey -- not implemented

        return getPublicKey(mode,message)


    elif case == 5: # If the cipherMode is set to Caesar -- implemented

        return getCaesarCipher(mode,message)


    else: # If no case is found

        print('No cipher mode found. Defaulting to AES.')
        return getAESCipher(mode,message)



def getShiftNum(mode): # Used in the Caesar Cipher to determine the amount of characters to shift the message.

    if mode[0] == 'e':
        shiftNumInput = input('\nHow many characters would you like to shift the message? (1-26) ')
    else:
        shiftNumInput = input('\nPlease enter your shift number. (1-26) ')
    try:
        shiftNum = int(shiftNumInput)

        if shiftNum >= 1 and shiftNum <= 26:

            return shiftNum

        else:

            sys.exit('\nSorry, but that\'s not in the specified range. Aborting.\n')

    except TypeError:

        sys.exit('\nSorry, but that\'s not an integer. Aborting.\n')

    except:

        sys.exit('Input error. Aborting.')

def getBlowfishCipher(mode,message):

    # This ain't implemented yet! Coming soon (possibly)
    sys.exit('Sorry, but blowfish encryption is not currently implemented. Available encryptions: %s' % currentEncryptions)

def getSHAHash(mode,message):

    # This ain't implemented yet! Coming soon (possibly)
    sys.exit('Sorry, but SHA hash encryption is not currently implemented. Available encryptions: %s' % currentEncryptions)

def getHMACHash(mode,message):

    # This ain't implemented yet! Coming soon (possibly)
    sys.exit('Sorry, but HMAC hash encryption is not currently implemented. Available encryptions: %s' % currentEncryptions)

def getPublicKey(mode,message):

    # This ain't implemented yet! Coming soon (possibly)
    sys.exit('Sorry, but public keys are not currently implemented. Available encryptions: %s' % currentEncryptions)

def getCaesarCipher(mode,message):

    key = getShiftNum(mode)

    encryptedMessage = ''

    if mode[0] == 'd':

        key = -key

    for symbol in message:

        if symbol.isalpha():

            num = ord(symbol)
            num += key

            if symbol.isupper():
                if num > ord('Z'):
                    num -= 26
                elif num < ord('A'):
                    num += 26
            elif symbol.islower():
                if num > ord('z'):
                    num -= 26
                elif num < ord('a'):
                    num += 26

            encryptedMessage += chr(num)
        else:
            encryptedMessage += symbol
    return encryptedMessage

def getCipherMode():
    cipherModeInput = input('\nPlease select your encryption mode. Available encryptions are: %s. ' % currentEncryptions).lower()

    if cipherModeInput in 'aes_advanced encryption standard_a'.split('_'):

        print('\nEncrypting with AES.')
        cipherMode = 0
        return cipherMode

    elif cipherModeInput in 'blowfish b blow fish'.split():

        print('\nEncrypting with Blowfish.')
        cipherMode = 1
        return cipherMode

    elif cipherModeInput in 'sha_sha-256_sha-512_sha-1_sha-2_sha-3_secure hash algorithm_secure hash_sha 256_ sha 512_sha 1_sha 2_sha 3'.split('_'):

        print('\nEncrypting with SHA.')
        cipherMode = 2
        return cipherMode

    elif cipherModeInput in 'hmac_hash-based message authentication code_hash based message authentication code_h'.split('_'):

        print('\nEncrypting with HMAC.')
        cipherMode = 3
        return cipherMode

    elif cipherModeInput in 'p_pk_k_public_key_public key'.split('_'):

        print('\nEncrypting with a public key.')
        cipherMode = 4
        return cipherMode

    elif cipherModeInput in 'caesar_cipher_cypher_caesar cipher_caesar cypher_shifter'.split('_'):

        print('\nEncrypting with a Caesar Cipher.')
        cipherMode = 5
        return cipherMode

    else:

        cipherMode = 20
        return cipherMode

def startup():
    print("\nP-Cypher Alpha starting up...\n\nv0.05 dev\nMateo Guynn\n2015\n")
    mode = getMode()

    message = getMessage()

    cipherMode = getCipherMode()
    if cipherMode != 5:
        try:
            open(getOutput(), 'wb').write(getEncrypt(mode,message,cipherMode))
        except IOError:
            sys.exit('Oh noes! Something has gone terribly wrong!')
        except FileNotFoundError:
            sys.exit('Your input file was not found.')
    else:
        try:
            open(getOutput(), 'w+').write(getEncrypt(mode,message,cipherMode))
        except IOError:
            sys.exit('Oh noes! Something has gone terribly wrong!')
        except FileNotFoundError:
            sys.exit('Your input file was not found.')

    print('\nDone.')

startup()

Aucun commentaire:

Enregistrer un commentaire