2
votes

Lutte avec la boucle ASCII pour le chiffrement César

C'est ma première question postée sur stackoverflow, toute aide / critique / assistance est appréciée ... J'ai besoin de toute l'aide que je peux obtenir haha.

Je suis très novice en programmation.

Le but est de créer un chiffrement César qui crypte et décrypte l'entrée de chaîne utilisateur dans un ord, ajoute l'entrée utilisateur offset_value , puis la change à nouveau en caractère.

J'utilise des caractères ASCII. Le problème est que je dois isoler le cryptage et le décryptage en ASCII 32 ('a') - ASCII 126 ('~'). Je ne sais pas comment créer une fonction qui recule à travers les 94 caractères.

Donc, par exemple, si le caractère est 'Z' qui est ASCII ord 94, si nous ajoutons l'entrée utilisateur offset_value , qui pourrait être 90 qui ferait l'ord 184. Ce qui est en dehors de la plage.

Cela conduit au vrai problème, le cryptage par force brute. Cela fonctionne ... en quelque sorte. Il doit afficher tous les résultats possibles avec la offset_value changeant entre 1 et 94. Par exemple, que se passe-t-il si nous déchiffrons chaque lettre avec un offset_value de x (x étant n'importe quel nombre de 1 à 94).

Au lieu de cela, il ne cesse de monter et de monter.

Est-ce que cela a du sens?

Mon code est ci-dessous. Je sais que je n'ai encore créé aucune fonction, mais je le ferai.

Merci d'avance les gars.

choice = 0
list1 = [1, 2, 3, 4]
list2 = list(range(1, 95))
new_ord = 0
index = 1
encryption = ''
decryption = ''
offset_value = 1


#while loop allows for multiple use, option 4 ends loop
while choice != 4:
    print('*** Menu ***')
    print('\r')
    print('1. Encrypt string')
    print('2. Decrypt string')
    print('3. Brute force decryption')
    print('4. Quit')
    print('\r')
    choice = int(input('What would you like to do [1,2,3,4]? '))

    #invalid user input loop, valid entry ends loop
    while choice not in list1:
        print('\r')
        print('Invalid choice, please enter either 1, 2, 3 or 4.')
        print('\r')
        choice = int(input('What would you like to do [1,2,3,4]? '))

    #user chooses 'encrypt string', stores data
    if choice == 1:
        print('\r')
        string_to_encrypt = str(input('Please enter string to encrypt: '))
        offset_value = int(input('Please enter offset value (1 to 94): '))

        #invalid user input loop, valid entry ends loop
        while offset_value not in list2:
            offset_value = int(input('Please enter offset value (1 to 94): '))

        #encryption loop for length of string_to_encrypt
        for letter in string_to_encrypt:
            encryption = encryption + chr((ord(letter) + offset_value))

        #prints encrypted string
        print('\r')
        print('Encrypted string:')
        print(encryption)
        print('\r')

        #clears ecryption data
        encryption = ''

    #user chooses 'decrypt string', stores data
    elif choice == 2:
        print('\r')
        string_to_decrypt = str(input('Please enter string to decrypt: '))
        offset_value = int(input('Please enter offset value (1 to 94): '))

        #invalid user input loop, valid entry ends loop
        while offset_value not in list2:
            offset_value = int(input('Please enter offset value (1 to 94): '))

        #decryption loop for length of string_to_decrypt
        for letter in string_to_decrypt:
                decryption = decryption + chr((ord(letter) - offset_value))

        #prints decrypted string
        print('\r')
        print('Decrypted string:')
        print(decryption)
        print('\r')

        #clears decryption data
        decryption = ''

    #user chooses 'Brute Force Decryption
    elif choice == 3:
        string_to_decrypt = str(input('Please enter string to decrypt: '))
        for number in range(94):
            for letter in string_to_decrypt:
                decryption = decryption + chr((ord(letter) - offset_value))
            print('Offset: ', index, '= Decrypted String: ', decryption)
            offset_value = offset_value + 1
            index = index + 1
            decryption = ''

    #user chooses 'quit'
        print('Goodbye.')


2 commentaires

32 sur la table ascii correspond à un espace (''), pas à 'a' (c'est 97). De plus, cette plage de caractères a 95 caractères, pas 94 (en supposant que 126 est inclusif).


Ah oui merci


3 Réponses :


2
votes

Plutôt que de réécrire votre programme à votre place, je vais vous décrire comment résoudre un problème comme celui-ci, ce qui implique principalement de faire un peu de calcul.

L'essence de votre question se résume à la façon de compenser un problème donné. plage de valeurs entières d'un montant donné et encapsulation des résultats afin qu'ils soient tous toujours dans la plage de valeurs d'origine.

Cela peut être fait mathématiquement pour chaque valeur de la plage en soustrayant la valeur minimale de la plage , en ajoutant le décalage à cela, en calculant la somme modulo la différence entre les valeurs haute et basse de la plage (+1), puis en ajoutant finalement la valeur minimale pour compenser sa soustraction initiale. La partie cruciale est l'opération modulo % pour contraindre la somme.

Le code de la fonction offset_range () ci-dessous montre comment faire tout cela dans un list comprehension qui est un moyen abrégé de créer des listes en Python.

Cela et le print_range () code> fonction sert également à illustrer comment les fonctions sont définies en Python.

ascii_range: [ 97,  98,  99, 100, 101, 102, 103, 104, 105, ... 122, 123, 124, 125, 126]
             ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', ... 'z', '{', '|', '}', '~']

offset_range: [ 98,  99, 100, 101, 102, 103, 104, 105, ... 122, 123, 124, 125, 126,  97]
              ['b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', ... 'z', '{', '|', '}', '~', 'a']

Sortie:

def print_range(name, range_):
    """ Pretty-print given range of character ordinals. """
    ints = ', '.join('{:3d}'.format(val) for val in range_)
    chrs = ', '.join('{:>}'.format(repr(chr(val))) for val in range_)
    print('{}: [{}]'.format(name, ints))
    print('{}  [{}]'.format(len(name)*' ', chrs))

def offset_range(offset_value, range_):
    """ Add offset value to integers in given range and wrap result. """
    min_val, max_val = range_[0], range_[-1]  # Get upper and lower limits.
    diff_plus_1 = (max_val - min_val) + 1  # Their difference + 1 for computation.
    # Use a list comprehension to create new list.
    offset_range = [(((val - min_val) + offset_value) % diff_plus_1) + min_val
                        for val in range_]
    return offset_range

ascii_range = list(range(ord('a'), ord('~')+1))
print_range('ascii_range', ascii_range)

print()
offset_range = offset_range(1, ascii_range)
print_range('offset_range', offset_range)


0 commentaires

1
votes

Le chiffrement César utilise un alphabet avec un index commençant à 0. Vous avez donc besoin d'une fonction pour en convertir un de la plage 32 à 126 à 0 à 126 - 32 = 98. Ensuite, vous devez effectuer des calculs mod 99 car il y a 99 éléments d'index 0 à 98.

Généralement, l'idée de la programmation est de vaincre en divisant le problème en plusieurs fonctions. La plupart des réponses supposeront déjà que vous parlez couramment et que vous ne le faites qu'une seule doublure. Cependant, cela ne fera généralement que vous embrouiller et vous permettra d'apprendre comment ne pas programmer.

J'ai donc écrit ci-dessous la procédure de cryptage . Maintenant, si vous créez la procédure de déchiffrement , vous pouvez réutiliser la méthode déchiffrer pour votre tentative de force brute. Vous verrez qu'il est maintenant beaucoup plus facile de faire les choses correctement.

LO_CHAR = ord('A')
HI_CHAR = ord('~')
CHARS = HI_CHAR - LO_CHAR + 1

def char_to_index(char):
    o = ord(char)
    if (o < LO_CHAR or o > HI_CHAR):
       raise
    return o - LO_CHAR

def index_to_char(index):
    if (index < 0 or index >= CHARS):
       raise
    return chr(index + LO_CHAR)

def encrypt_index(key, index):
    return (index + key) % CHARS

def encrypt(key, plaintext):
    ciphertext = ''
    for plain_char in plaintext:
        plain_index = char_to_index(plain_char)
        cipher_index = encrypt_index(key, plain_index)
        cipher_char = index_to_char(cipher_index)
        ciphertext = ciphertext + cipher_char
    return ciphertext

... bonne programmation ...


1 commentaires

Et oui, parce que j'ai toujours un petit problème de démarrage avec Python, j'ai eu une augmentation lors de la création de ce programme. Vous montre simplement pourquoi vous avez besoin de déclarations de garde comme ça.



1
votes

Certaines solutions utilisant ordinal% len (alphabet) ont déjà été proposées. Je veux présenter une solution légèrement différente. Toute la solution tourne autour de str.maketrans , qui peut fournir un moyen facile d'implémenter des chiffrements.

Si vous passez deux chaînes de longueur égale dans str.maketrans , il renvoie un dictionnaire qui mappe les valeurs ordinales des caractères du premier, aux valeurs ordinales des caractères du second dans l'ordre dans lequel ils apparaissent.

Par exemple:

def get_alphabet(min_ordinal, max_ordinal):
    assert isinstance(min_ordinal, int)
    assert isinstance(max_ordinal, int)
    assert 0 <= min_ordinal <= 255
    assert 0 <= max_ordinal <= 255
    assert min_ordinal <= max_ordinal
    return "".join(map(chr, range(min_ordinal, max_ordinal+1)))

def get_offset_alphabet(alphabet, *, offset):
    assert isinstance(alphabet, str)
    assert isinstance(offset, int)
    assert alphabet

    from itertools import cycle, islice
    char_iterator = cycle(alphabet)
    _ = list(islice(char_iterator, [offset, len(alphabet)+offset][offset<0]))
    return "".join(islice(char_iterator, len(alphabet)))

def main():

    from_alphabet = get_alphabet(32, 126)
    to_alphabet = get_offset_alphabet(from_alphabet, offset=1)

    translation = str.maketrans(from_alphabet, to_alphabet)

    plaintext = "Hello World"
    assert all(ord(char) in translation for char in plaintext)

    ciphertext = plaintext.translate(translation)
    print(ciphertext)

    return 0


if __name__ == "__main__":
    import sys
    sys.exit(main())

Majuscule 'A' (65) -> Majuscule 'D' (68). Etc. Vous pouvez déjà voir comment cela pourrait être utilisé pour implémenter votre César-cipher - tant que vous pouvez générer deux chaînes de longueur égale, où la deuxième chaîne est décalée d'un certain montant par rapport à la première (puis s'enroule vers au début), vous pouvez facilement générer l'une de ces tables de traduction (la documentation l'appelle ainsi à plusieurs endroits, mais c'est juste un vieux dictionnaire Python) et lui faire faire la plupart du gros du travail.

Mon solution:

get_alphabet , étant donné un ordinal minimum et maximum, renvoie une chaîne contenant tous les caractères de l'alphabet souhaité.

get_offset_alphabet code>, étant donné un alphabet (chaîne) et un offset (entier), retourne le passé en alphabet mais offset et enveloppé. Ceci est réalisé en créant un itérateur de caractères sans fin en utilisant itertools.cycle , en découpant et en supprimant une certaine quantité du début de l'itérateur en fonction de l'offset, puis en construisant une chaîne de longueur égale à partir du suivant n-caractères de l'itérateur (où «n» est la longueur de l'alphabet d'origine). Cela prend également en charge les décalages négatifs.

Dans le main , nous obtenons nos deux alphabets - le premier est inchangé et le second est décalé et enveloppé. Nous créons une table de traduction (dictionnaire), affirmons que tous les caractères de notre texte en clair peuvent être mappés à un autre caractère selon notre table de traduction, puis effectuons la traduction réelle pour obtenir le texte chiffré. str.maketrans a également un troisième paramètre facultatif qui peut être utilisé pour mapper des caractères qui n'ont pas de correspondance avec Aucun , dont je n'ai pas profité ici. p >

>>> str.maketrans("ABC", "DEF")
{65: 68, 66: 69, 67: 70}
>>> 


0 commentaires