Partage
  • Partager sur Facebook
  • Partager sur Twitter

Chiffrement - Déchiffrement - Crack

César - Vigénère

    18 octobre 2011 à 19:31:35

    Bonjour,

    J'ai essayé de faire un exercice du cours de C++ en python, pour m'entrainer, celui de Novembre 2008 sur le chiffre de César.

    J'ai donc la possibilité de coder / décoder en connaissant la clé et de cracker le code sans connaitre la clé, soit par une brute force soit par analyse fréquentielle. C'est sur cette dernière que j'ai un soucis car mon code se base sur le 'e' en lettre la plus fréquente et ce n'est pas toujours correct. J'ai regardé les codes sur wiki et ailleurs mais ils ne sont pas commentés et donc durs à déchiffrer :colere2:

    Que me proposez-vous pour améliorer ça ?
    Y a-t-il une meilleur façon de faire mon menu que par des elif en série ?
    Qu'elle est la meilleur méthode pour faire la table de convertion ?
    tab_code = letters[dec:] + letters[:dec]
    # ou
    tab_code = ''.join([decal(c, dec) for c in letters])
    


    C'est du python 3 (les modes crack et brute force, cachés, sont accessibles par 3 et 4):
    # -*- coding:utf-8 -*-
    
    # permet de chiffrer/déchiffrer un message avec le chiffre de César
    # c'est à dire le décallage de x lettres dans l'alphabet
    from string import ascii_lowercase as letters
    
    def saisie_dec(flag):
        """Demande à l'utilisateur le décalage et le retourne"""
        while 1:    # contrôle de la saisie
            try:
                dec = int(input("Entre le décalage (entre 1 et 25) : "))
                if dec >= 1 or dec <= 25:
                    break
                else:
                    print("Un chiffre entre 1 et 25 j'ai dit !")
            except ValueError:
                print("Un chiffre entre 1 et 25 j'ai dit !")
        if flag == "déchiffrer":
            dec = -dec
        return dec
    
    
    def saisie_phrase(flag):
        """Demande à l'utilisateur la phrase et la retourne"""
        while 1:
            try:
                phrase = input("Entre la phrase à {} : ".format(flag)).lower()
                break
            except ValueError:
                print("Phrase non correcte")
        return phrase
    
    
    def decal(c, dec):
        """Décalle la lettre c de dec et retourne le résultat"""
        c = (letters.find(c) + dec) % len(letters)
        return letters[c]
    
    
    def crack(phrase):
        """Crack une phrase chiffrée avec le chiffre de César"""
    
        # fréquence d'apparition des lettres en Français
        # merci wikipédia : http://fr.wikipedia.org/wiki/Analyse_fréquentielle
        FR_Freq = {'a':9.2,'b':1.02,'c':2.64,'d':3.39,'e':15.87,'f':0.95,'g':1.04,\
                   'h':0.77,'i':8.41,'j':0.89,'k':0.00,'l':5.34,'m':3.24,'n':7.15,\
                   'o':5.14,'p':2.86,'q':1.06,'r':6.46,'s':7.90,'t':7.26,'u':6.24,\
                   'v':2.15,'w':0.00,'x':0.30,'y':0.24,'z':0.32}
    
        #on remplit un dico avec les lettres présentes et on les compte
        dic = {}
        for c in phrase:
            if c == ' ':    # on ne répertorie pas les espaces
                pass
            elif c in dic:
                dic[c] = dic.get(c) + 1
            else:
                dic[c] = 1
    
        # on remplace le nombre d'occurence des lettres par un %age
        for cle, valeur in dic.items():
            val = ( valeur * 100 ) / len(phrase)
            val = float((int( val * 100 )) / 100)    #que 2 chiffes après la virgule
            dic[cle] = val
    
        c_max = max(dic.values())
        fr_max = max(FR_Freq.values())
        dec = letters.find([c for c,v in dic.items() if v == c_max ][0])\
              - letters.find([c for c,v in FR_Freq.items() if v == fr_max ][0])
    
        tab_code = letters[dec:] + letters[:dec]
    
        return phrase.translate(str.maketrans(tab_code, letters))
    
    def brute_force(phrase):
        """Brute force une phrase chiffrée avec le chiffre de César"""
        for i in range(26):
            tab_code = letters[i:] + letters[:i]
            print(phrase.translate(str.maketrans(tab_code, letters)))
    
    
    
    def application():
        """Coeur du programme"""
        print("Programme de chiffrement / déchiffrement\
     avec la méthode du chiffre de César")
    
        while 1:
            choix = int(input("Entre 1 pour chiffrer, 2 pour déchiffrer,\
     0 pour quitter : "))
    
            if choix == 1:
                print("Chiffrement")
                phrase, dec = saisie_phrase("chiffrer"), saisie_dec("chiffrer")
                tab_code = letters[dec:] + letters[:dec]
                # tab_code = ''.join([decal(c, dec) for c in letters]) #variante
                print(phrase.translate(str.maketrans(letters, tab_code)))
    
            elif choix == 2:
                print("Déchiffrement")
                phrase, dec = saisie_phrase("déchiffrer"), saisie_dec("déchiffrer")
                tab_code = ''.join([decal(c, dec) for c in letters])
                print(phrase.translate(str.maketrans(tab_code, letters)))
    
            elif choix == 3:
                print("Crack - Hidden mode")
                phrase = saisie_phrase('cracker').strip('\r')
                print(crack(phrase))
    
            elif choix == 4:
                print("Brute force - Hidden mode")
                phrase = saisie_phrase('brute forcer').strip('\r')
                brute_force(phrase)
    
            elif choix == 0:
                print("Fin du programme")
                break
    
            else:
                print("J'ai dis 1 ou 2 !")
    
    if __name__ == '__main__':
        application()
    
    • Partager sur Facebook
    • Partager sur Twitter
      19 octobre 2011 à 18:28:02

      Pour le « crack », plutôt que de te baser uniquement sur le e, tu peux établir les fréquences de chaque lettre et minimiser une distance avec les fréquences des lettres en français.
      • Partager sur Facebook
      • Partager sur Twitter
        20 octobre 2011 à 10:48:05

        Citation : Seren

        Pour le « crack », plutôt que de te baser uniquement sur le e, tu peux établir les fréquences de chaque lettre et minimiser une distance avec les fréquences des lettres en français.


        J'y pensais mais je ne vois pas trop comment faire à vrai dire. Je vais continuer à chercher
        • Partager sur Facebook
        • Partager sur Twitter
          20 octobre 2011 à 11:49:55

          Bah, c'est pas compliqué : pour chaque code possible, tu calcules la fréquence des lettres en sortie et tu prends celui qui se rapproche le plus des fréquences en français. Pour savoir lequel est le plus proche, tu peux prendre par exemple la distance euclidienne (la somme des carrés des différences entre chaque fréquence).
          • Partager sur Facebook
          • Partager sur Twitter
            21 octobre 2011 à 12:17:43

            Citation : Seren

            Bah, c'est pas compliqué : pour chaque code possible, tu calcules la fréquence des lettres en sortie et tu prends celui qui se rapproche le plus des fréquences en français. Pour savoir lequel est le plus proche, tu peux prendre par exemple la distance euclidienne (la somme des carrés des différences entre chaque fréquence).


            Pas compliqué, sur le papier, oui, mais à programmer, je ne vois pas comment faire. Il faut que je calcule la distance euclidienne pour les lettres en français puis en chiffré, mais comment les comparer ?

            J'ai contourné en laissant le choix de lettre à l'utilisateur :
            def crack(phrase):
                """Crack une phrase chiffrée avec le chiffre de César"""
            
                #on remplit un dico avec les lettres présentes et on les compte
                dic = {}
                for c in phrase:
                    if c == ' ':    # on ne répertorie pas les espaces
                        pass
                    elif c in dic:
                        dic[c] = dic.get(c) + 1
                    else:
                        dic[c] = 1
            
                # on remplace le nombre d'occurence des lettres par un %age
                for cle, valeur in dic.items():
                    val = ( valeur * 100 ) / len(phrase)
                    val = float((int( val * 100 )) / 100)    #que 2 chiffes après la virgule
                    dic[cle] = val
            
                print("Fréquence des lettres chiffrées :")
                for c, v in dic.items():
                      print(c, ' - ', v, '%')
            
                print("En Français, les lettres e, a, i, s, t, n, sont les plus fréquentes.")
                while 1:
                    while 1:
                        try:
                            l = input("Essayer avec quelle lettre ? ").lower().strip("\r")
                            if l in ['e','a','i','s','t','n']:
                                break
                            else:
                                print("Entre soit e, a, i , s, t ou n")
                        except ValueError:
                            print("Une lettre j'ai dis")
            
                    # calcul du décalage entre les deux
                    c_max = max(dic.values())    # lettre chiffrée la plus fréquente
                    dec = letters.find([c for c,v in dic.items() if v == c_max ][0])\
                          - letters.find(l)
            
                    tab_code = letters[dec:] + letters[:dec]
            
                    print(phrase.translate(str.maketrans(tab_code, letters)))
            
                    choix = input("1 pour réessayer, 0 pour terminer : ").strip("\r")
                    if choix==0:
                        break
            


            Mais ça ne me satisfait pas, je veux que ce soit le code qui trouve tout seul. Ce n'est peut-être pas possible sans comparaison des résultats à un dictionnaire pour voir si les mots trouvés existent vraiment ?
            J'ai étudié le code de wikipédia, et il ne prend comme base que le 'e', dans un texte sans 'e' il ne trouve rien de bon. Et je n'ai pas trouvé d'autres codes de ce type.
            • Partager sur Facebook
            • Partager sur Twitter
              27 octobre 2011 à 19:08:05

              Bon, je reviens à la charge ;)

              J'ai attaqué le chiffre de Vigénère. Chiffrage/Déchiffrage sans soucis. Mais je bloque encore sur l'analyse fréquentielle. Pas de problèmes pour la faire, je sors les fréquences des lettres, mais je n'arrive pas à voir comment interpréter les résultats. Je comprends comment le faire sur papier mais avec du code, pas moyen. Et je n'ai pas trouvé de code python le faisant pour voir comment faire.

              J'aimerais un petit peu d'aide :-°

              Mon code, avec la fonction trouve_cle() pas finie donc.
              # -*- coding:utf-8 -*-
              
              # permet de chiffrer/déchiffrer un message avec la méthode de Vigenère
              # on assigne une valeur à chaque lettre, a=0, b=1...
              # on utilise une clé et on décale la n ème lettre du message de la
              # valeur de la n ème lettre de la clé (cette dernière doit être répétée)
              # sur la longueur du message
              
              from string import ascii_lowercase as letters
              
              
              def chiffre(phrase, cle, flag=0):
                  """Chiffre ou déchiffre 'phrase' avec 'cle' selon la méthode de Vigenère\
                  et renvoi phrase_traitee\
                  si flag = 1 chiffre sinon déchiffre"""
              
                  i = 0    # compteur pour la cle
                  phrase_traitee = ""
              
                  for c in phrase:
                      if c in " '":    # on ignore les espaces et apostrophes
                          phrase_traitee += c
                          continue
                      if i == len(cle)-1:    # si on arrive au bout de la clé, on la reset
                          i = 0
              
                      l = letters.find(c)    # valeur de c
                      lc = letters.find(cle[i])    # valeur de i
              
                      if flag:    # on chiffre
                      # valeur de c + valeur de i, % 26, donne la valeur de la lettre chiffree
                          phrase_traitee += letters[(l + lc) % 26]
                      else:    # on déchiffre
                          if lc > l:
                              phrase_traitee += letters[(26 - lc) + l]
                          else:
                              phrase_traitee += letters[l - lc]
              
                      i+=1
              
                  return phrase_traitee
              
              
              def trouve_cle(phrase):
                  """Découpe la phrase en modules de longueur lcle
                  et fait une analyse fréquentielle
                  retourne la cle"""
              
                  while 1:
                      try:
                          lcle = int(input("Entre la longueur de la clé : "))
                          break
                      except ValueError:
                          print("Entre un chiffre !")
              
                  # ne garde que les lettres
                  phrase = ''.join(l for l in phrase if l in letters)
              
                  #phrase = phrase.replace(" ", "")    # enleve les espaces
                  #phrase = phrase.replace("'", "")    # enleve les apostrophes
              
                  # on groupe les lettres de phrase par correpondance de la clé
                  tab_lettres = []
                  for i in range(lcle):
                      n = 0
                      lettre_prov = ""
                      while n+i < len(phrase):
                          lettre_prov += phrase[n+i]
                          n += lcle
                      tab_lettres.append(lettre_prov)
              
                  tab_freq = []    # pour les fréquences des lettres dans tab_lettres
                  for e in tab_lettres:
                      #on remplit un dico avec les lettres présentes et on les compte
                      dic = {}    #dico temporaire
                      for c in e:
                          if c in dic:
                              dic[c] = dic.get(c) + 1
                          else:
                              dic[c] = 1
              
                      # on remplace le nombre d'occurence des lettres par un %age
                      for cle, valeur in dic.items():
                          val = ( valeur * 100 ) / len(e)
                          val = float((int( val * 100 )) / 100)    #que 2 chiffes après la virgule
                          dic[cle] = val
              
                      tab_freq.append(dic)
              
              
              def enleve_accents(phrase):
                  """enlève l'accent, ou la cédille de la phrase"""
                  ch_acc = "àâäçéèêëîïôöùûü"
                  ch_s_acc = "aaaceeeeiioouuu"
                  for i in ch_acc:
                      if i in phrase:    # si accent dans phrase on l'enlève
                          phrase = phrase.replace(i, ch_s_acc[ch_acc.index(i)])
                          print(phrase)
                  return phrase
              
              
              def saisie(action):
                  """Pour saisir la phrase et la clé et les retourne
                  Enlève aussi les accents avec la fonction enleve_accents()
                  Prend en paramètre action : chiffrer ou déchiffrer"""
                  while 1:
                      try:
                          phrase = input("Entre la phrase à {} : ".format(action))\
                                   .lower().strip("\r")
                          phrase = enleve_accents(phrase)
              
                          cle = input("Entre la clé : ".lower().strip("\r"))
                          cle = enleve_accents(cle)
              
                          return phrase, cle
              
                      except ValueError:
                          print("Phrase non correcte")
              
              
              def programme_principal():
                  """Tout est dans le nom de la fonction"""
              
                  print("Programme de chiffrement / déchiffrement\
               avec la méthode de Vigenère")
              
                  while 1:
                      print("Entre ton choix :\n[0] - Quitter\n[1] - Chiffrer\n[2] - Déchiffrer")
                      choix = int(input("-> "))
              
                      if choix == 1:
                          print("Chiffrement")
                          phrase, cle = saisie("chiffrer")
                          print("Phrase chiffrée : {}".format(chiffre(phrase, cle, 1)))
              
                      elif choix == 2:
                          print("Déchiffrement")
                          phrase, cle = saisie("déchiffrer")
                          print("Phrase déchiffrée : {}".format(chiffre(phrase, cle)))
              
                      elif choix == 3:
                          print("Crack - Hidden mode")
              
                      elif choix == 4:
                          print("Brute force - Hidden mode")
              
                      elif choix == 0:
                          print("Fin du programme")
                          break
              
                      else:
                          print("J'ai dis 0, 1 ou 2 !")
              
              
              if __name__ == "__main__":
                  programme_principal()
              
              • Partager sur Facebook
              • Partager sur Twitter
                30 octobre 2011 à 11:06:10

                Je cherche toujours :(

                J'ai fais un raccourcis en prenant le 'e' comme base, quand on connait la longueur de la clé, mais ce n'est pas bon. Je pourrais essayer toutes les possibilités de clé possible, avec les lettres les plus fréquentes, et comparer les résultats avec un dictionnaire de mots pour voir quand je tombe juste, mais c'est un peu lourd comme méthode.
                Il n'y a pas moyen de trouver un code d'analyse fréquentielle qui exploite les résultats correctement, sans se baser sur le 'e' par défaut ?

                Mon code, en python 3 :
                # -*- coding:utf-8 -*-
                
                # permet de chiffrer/déchiffrer un message avec la méthode de Vigenère
                # on assigne une valeur à chaque lettre, a=0, b=1...
                # on utilise une clé et on décale la n ème lettre du message de la
                # valeur de la n ème lettre de la clé (cette dernière doit être répétée)
                # sur la longueur du message
                
                from string import ascii_lowercase as letters
                
                
                def chiffre(phrase, cle, flag=0):
                    """Chiffre ou déchiffre 'phrase' avec 'cle' selon la méthode de Vigenère\
                    et renvoi phrase_traitee\
                    si flag = 1 chiffre sinon déchiffre"""
                
                    i = 0    # compteur pour la cle
                    phrase_traitee = ""
                
                    for c in phrase:
                        if c in " '":    # on ignore les espaces et apostrophes
                            phrase_traitee += c
                            continue
                        if i == len(cle):    # si on arrive au bout de la clé, on la reset
                            i = 0
                
                        l = letters.find(c)    # valeur de c
                        lc = letters.find(cle[i])    # valeur de i
                
                        if flag:    # on chiffre
                        # valeur de c + valeur de i, % 26, donne la valeur de la lettre chiffree
                            phrase_traitee += letters[(l + lc) % 26]
                        else:    # on déchiffre
                            if lc > l:
                                phrase_traitee += letters[(26 - lc) + l]
                            else:
                                phrase_traitee += letters[l - lc]
                
                        i+=1
                
                    return phrase_traitee
                
                
                def trouve_cle(phrase):
                    """Découpe la phrase en modules de longueur lcle
                    et fait une analyse fréquentielle
                    retourne la cle"""
                
                    cle = ""
                
                    while 1:
                        try:
                            lcle = int(input("Entre la longueur de la clé : "))
                            break
                        except ValueError:
                            print("Entre un chiffre !")
                
                    # ne garde que les lettres
                    phrase = ''.join(l for l in phrase if l in letters)
                
                    # on groupe les lettres de phrase par correpondance de la clé
                    tab_lettres = []
                    for i in range(lcle):
                        n = 0
                        lettre_prov = ""
                        while n+i < len(phrase):
                            lettre_prov += phrase[n+i]
                            n += lcle
                        tab_lettres.append(lettre_prov)
                
                    # analyse fréquentielle
                    tab_freq = []    # pour les fréquences des lettres dans tab_lettres
                    for e in tab_lettres:
                        #on remplit un dico avec les lettres présentes et on les compte
                        dic = {}    #dico temporaire
                        for c in e:
                            if c in dic:
                                dic[c] = dic.get(c) + 1
                            else:
                                dic[c] = 1
                
                        # on remplace le nombre d'occurence des lettres par un %age
                        for c, v in dic.items():
                            val = ( v * 100 ) / len(e)
                            val = float((int( val * 100 )) / 100)#que 2 chiffes après la virgule
                            dic[c] = val
                
                        tab_freq.append(dic)
                
                    # analyse des écarts entre les fréquences de lettre
                    # lettres les plus fréquentes en français : e, a, i, s, t, n, r
                    # écarts respectifs : 4, 8, 10, 1, 6, 4
                    liste_lettres_francais = ['e', 'a', 'i', 's', 't', 'n', 'r']
                
                    for dic in tab_freq:
                        liste_lettres_chiffrees = []
                        for i in range(7):
                            # analyse le dico et fait une liste des 7 lettres les plus fréquentes
                            liste_lettres_chiffrees.append([c for c, v in dic.items()
                                                            if v == max(dic.values())][0])
                            # l'index [0] est là car une list_comprehension retourne une liste
                            del dic[liste_lettres_chiffrees[-1]]    # on efface la + fréquente
                
                        # comme je n'arrive pas à faire un algo pour exploiter les résultats,
                        # je prend e comme lettre de base
                        dec = letters.find(liste_lettres_chiffrees[0]) - \
                              letters.find(liste_lettres_francais[0])
                
                        cle += letters[dec]
                
                    return cle
                
                
                def enleve_accents(phrase):
                    """enlève l'accent, ou la cédille de la phrase"""
                    ch_acc = "àâäçéèêëîïôöùûü"
                    ch_s_acc = "aaaceeeeiioouuu"
                    for i in ch_acc:
                        if i in phrase:    # si accent dans phrase on l'enlève
                            phrase = phrase.replace(i, ch_s_acc[ch_acc.index(i)])
                    return phrase
                
                
                def saisie(action):
                    """Pour saisir la phrase et la clé et les retourne
                    Enlève aussi les accents avec la fonction enleve_accents()
                    Prend en paramètre action : chiffrer ou déchiffrer"""
                    while 1:
                        try:
                            phrase = input("Entre la phrase à {} : ".format(action))\
                                     .lower().strip("\r")
                            phrase = enleve_accents(phrase)
                
                            cle = input("Entre la clé : ").lower().strip("\r")
                            cle = enleve_accents(cle)
                
                            return phrase, cle
                
                        except ValueError:
                            print("Phrase non correcte")
                
                
                def programme_principal():
                    """Tout est dans le nom de la fonction"""
                
                    print("Programme de chiffrement / déchiffrement\
                 avec la méthode de Vigenère")
                
                    while 1:
                        print("Entre ton choix :\n[0] - Quitter\n[1] - Chiffrer\n[2] - Déchiffrer")
                        choix = int(input("-> "))
                
                        if choix == 1:
                            print("Chiffrement")
                            phrase, cle = saisie("chiffrer")
                            print("Phrase chiffrée : {}".format(chiffre(phrase, cle, 1)))
                
                        elif choix == 2:
                            print("Déchiffrement")
                            phrase, cle = saisie("déchiffrer")
                            print("Phrase déchiffrée : {}".format(chiffre(phrase, cle)))
                
                        elif choix == 3:
                            print("Crack - Hidden mode")
                            while 1:
                                try:
                                    phrase = input("Entre la phrase à cracker : ")\
                                             .lower().strip("\r")
                                    phrase = enleve_accents(phrase)
                                    break
                                except ValueError:
                                    print("Phrase non correcte")
                            cle = trouve_cle(phrase)
                            print("La clé est {}".format(cle))
                            print("Phrase déchiffrée : {}".format(chiffre(phrase, cle)))
                
                        elif choix == 4:
                            print("Brute force - Hidden mode")
                
                        elif choix == 0:
                            print("Fin du programme")
                            break
                
                        else:
                            print("J'ai dis 0, 1 ou 2 !")
                
                
                if __name__ == "__main__":
                    programme_principal()
                


                Je vais attaquer la détermination de la longueur de clé maintenant, je reviendrai là-dessus après car ça m’énerve de stagner et d'essayer des trucs sans savoir.
                • Partager sur Facebook
                • Partager sur Twitter

                Chiffrement - Déchiffrement - Crack

                × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                • Editeur
                • Markdown