def hackAffine(message):
    print 'Hacking...'

    # Python programs can be stopped at any time by pressing Ctrl-C (on
    # Windows) or Ctrl-D (on Mac and Linux)
    print '(Press Ctrl-C or Ctrl-D to quit at any time.)'

    # brute-force by looping through every possible key
    for key in range(len(affineCipherAbhi.SYMBOLS) ** 2):
        keyA = affineCipherAbhi.getKeyParts(key)[0]
        if Euclid_Algorithm.find_gcf(keyA, len(affineCipherAbhi.SYMBOLS)) != 1:
            continue

        decryptedText = affineCipherAbhi.decryptMessage(key, message)
        if not SILENT_MODE:
            print('Tried Key %s... (%s)' % (key, decryptedText[:40]))

        if detectEnglish.isEnglish(decryptedText):
            # Check with the user if the decrypted key has been found.
            print
            print 'Possible encryption hack:'
            print 'Key: %s' % (key)
            print 'Decrypted message: ' + decryptedText[:200]
            print
            print 'Enter D for done, or just press Enter to continue hacking:'
            response = raw_input('> ')

            if response.strip().upper().startswith('D'):
                return decryptedText
    return None
def getRandomKey():
    while True:
        keyA = random.randint(2, len(SYMBOLS))
        keyB = random.randint(2, len(SYMBOLS))
        
        if Euclid_Algorithm.find_gcf(keyA, len(SYMBOLS) -1) == 1:
            return keyA * len(SYMBOLS) + keyB
def decryptMessage(key, message):
    keyA, keyB = getKeyParts(key)
    checkKeys(keyA, keyB, 'decrypt')
    plaintext = ''
    modInverseOfKeyA = Euclid_Algorithm.find_modular_inverse(keyA, len(SYMBOLS))

    for symbol in message:
        if symbol in SYMBOLS:
            # decrypt this symbol
            symIndex = SYMBOLS.find(symbol)
            plaintext += SYMBOLS[(symIndex - keyB) * modInverseOfKeyA % len(SYMBOLS)]
        else:
            plaintext += symbol # just append this symbol undecrypted
    return plaintext
def checkKeys(keyA, keyB, mode):
    if keyA == 1 and mode == 'encrypt':
        # this is due to the index being multiplied by keyA (1)
        sys.exit('The affine cipher becomes incredibly weak when key A is \
        set to 1. Choose a different key.')
    
    if keyB == 0 and mode == 'encrypt':
        # this is due to the index being added by keyB (0)
        sys.exit('The affine cipher becomes incredibly weak when key B is \
        set to 0. Choose a different key.')
        
    if keyA < 0 or keyB < 0 or keyB > len(SYMBOLS) - 1:
        sys.exit('Key A must be greater than 0 and Key B must be between 0 \
        and %s.' % (len(SYMBOLS) - 1))
        
    if Euclid_Algorithm.find_gcf(keyA, len(SYMBOLS)) != 1:
        sys.exit('Key A (%s) and the symbol set size (%s) are not \
        relatively prime. Choose a different key.' % (keyA, len(SYMBOLS)))
# Inspired by Hacking with Ciphers Al Sweigart's book
# This program proves that the keyspace of the affine cipher is limited
# to len(SYMBOLS) ^ 2. (in theory)
# len(symbols) for key A and len(symbols) for key B 
# in reality, we also discard some of the sample space for its incompatibility of being relatively prime

import affineCipherAbhi, Euclid_Algorithm 

message = 'Make things as simple as possible, but not simpler.'
for keyA in range(2, 100):
    key = keyA * len(affineCipherAbhi.SYMBOLS) + 1# 1 represents keyB

    if Euclid_Algorithm.find_gcf(keyA, len(affineCipherAbhi.SYMBOLS)) == 1:
        print keyA, affineCipherAbhi.encryptMessage(key, message)