Κώδικας του Καίσαρα

Εισαγωγή

Στο μάθημα της Γ' Λυκείου Ειδικά Θέματα στο Υλικό και στα Δίκτυα Υπολογιστών οι μαθητές των ΕΠΑΛ έρχονται σε επαφή με την κρυπτογραφία και με τον κώδικα του Καίσαρα. Το θέμα αυτού του μαθησιακού πόρου είναι η υλοποίηση του κώδικα του Καίσαρα με Python. Ο λόγος που επιλέχθηκε η Python είναι πως οι μαθητές των ΕΠΑΛ διδάσκονται αυτή την γλώσσα προγραμματισμού και την εξετάζονται σε πανελλαδικό επίπεδο.

Ο κώδικας του Καίσαρα είναι κώδικας αντικατάστασης στον οποίο κάθε γράμμα του κειμένου αντικαθίσταται από κάποιο άλλο γράμμα με σταθερή απόσταση κάθε φορά στο αλφάβητο.

Για να υλοποίησουμε κρυπτογράφηση με τον αλγόριθμο του Καίσαρα θα πρέπει να υλοποιήσουμε την κωδικοποίηση και την αποκωδικοποίηση. Ας ξεκινήσουμε με την κωδικοποίηση.

Κωδικοποίηση

Η λογική της κωδικοποίησης είναι να αντικατασταθεί το κάθε γράμμα με το μετατοπισμένο του. Θα κατασκευάσουμε μια λίστα με όλα τα γράμματα στα ελληνικά κεφαλαία.

Η λίστα των γραμμάτων

        
            ab = 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ'
        
    
Αν έχεις ένα γράμμα π.χ. το Λ η θέση του στη λίστα ab μπορεί να υπολογιστεί με την εντολή index ως εξή ab.index('Λ').

Το παρακάτω πρόγραμμα εμφανίζει τις θέσεις των γραμμάτων της λέξης ΚΑΛΗΜΕΡΑ:

        
            ab = 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ'
            keimeno = 'ΚΑΛΗΜΕΡΑ'
            for l in keimeno:
              print(ab.index(l))
        
    
Για να πάμε στο επόμενο γράμμα από αυτό που έχουμε θα προσθέσουμε 1 στη θέση που υπολογίσαμε πριν.

Το παρακάτω πρόγραμμα εμφανίζει το επόμενο γράμμα από κάθε γράμμα της λέξης ΚΑΛΗΜΕΡΑ:

        
            ab = 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ'
            keimeno = 'ΚΑΛΗΜΕΡΑ'
            for l in keimeno:
              thesi = ab.index(l)
              neathesi = (ab.index(l) + 1) % len(ab)
              print(ab[neathesi])
        
    
Η νέα θέση είναι ab.index(l) + 1 αλλά θα πρέπει να προσέξουμε το Ω που θα πρέπει να αντικατασταθεί με το Α. Για αυτό το λόγο βάζουμε το υπόλοιπο της διαίρεσης με το μήκος της λίστας. Στη συγκεκριμένη περίπτωση το ab.index('Ω') είναι 23 προσθέτοντας 1 προκύπτει 24. Η πράξη (ab.index(l) + 1) % len(ab) θα δώσει σαν αποτέλεσμα το 0 και το ab[0] είναι το Α.
Αφού έχουμε τα γράμματα με τα οποία πρέπει να αντικατασταθούν τα αρχικά γράμματα μπορούμε να τα συνενώσουμε σε ένα αλφαριθμητικό που θα είναι το κωδικοποιημένο κείμενο.
        
            ab = 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ'
            keimeno = 'ΚΑΛΗΜΕΡΑ'
            kwdikopoiimeno = ''
            for l in keimeno:
              thesi = ab.index(l)
              neathesi = (ab.index(l) + 1) % len(ab)
              kwdikopoiimeno += ab[neathesi]
            print(kwdikopoiimeno)
        
    
Ας βάλουμε τη βασική λειτουργία της κωδικοποιήσης σε μια συνάρτηση με όνομα kaisaras_kwd:
        
            ab = 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ'
            keimeno = 'ΚΑΛΗΜΕΡΑ'
            def kaisaras_kwd(keimeno):
                kwdikopoiimeno = ''
                for l in keimeno:
                    thesi = ab.index(l)
                    neathesi = (ab.index(l) + 1) % len(ab)
                    kwdikopoiimeno += ab[neathesi]
                return(kwdikopoiimeno)
            print(kaisaras_kwd(keimeno))
        
    
Η απόσταση που έχουμε ορίσει είναι 1. Αλλά το πρόγραμμα δουλεύει και με διαφορετικές αποστάσεις. Μπορούμε να την βάλουμε την απόσταση σαν όρισμα της συνάρτησης d.
        
            ab = 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ'
            keimeno = 'ΚΑΛΗΜΕΡΑ'
            def kaisaras_kwd(keimeno,d):
                kwdikopoiimeno = ''
                for l in keimeno:
                    thesi = ab.index(l)
                    neathesi = (ab.index(l) + d) % len(ab)
                    kwdikopoiimeno += ab[neathesi]
                return(kwdikopoiimeno)
            print(kaisaras_kwd(keimeno,1))
            print(kaisaras_kwd(keimeno,2))
        
    
Η ίδια λέξη κωδικοποιημένη με απόσταση 2.
Στο τελικό βήμα ο χρήστης μπορεί να δώσει το κείμενο για κωδικοποίηση (χωρίς κενά) και την απόσταση που θέλει και να πάρει σαν αποτέλεσμα το κωδικοποιημένο κείμενο.
        
            ab = 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ'
            keimeno = input('Δώστε κείμενο για κρυπτογράφηση: ')
            d = int(input('Δώστε απόσταση κώδικα: '))
            def kaisaras_kwd(keimeno,d):
                kwdikopoiimeno = ''
                for l in keimeno:
                    thesi = ab.index(l)
                    neathesi = (ab.index(l) + d) % len(ab)
                    kwdikopoiimeno += ab[neathesi]
                return(kwdikopoiimeno)
            print(kaisaras_kwd(keimeno,d))
        
    
Αν προχωρήσετε στην Python λίγο περισσότερο θα δείτε πως η κωδικοποίηση μπορεί να γίνει σε μια γραμμή ως εξής:
    ''.join([ab[(ab.index(l) + d) % len(ab)] for l in keimeno])

Αποκωδικοποίηση

Η αποκωδικοποίηση είναι η αντίστροφη διαδικασία:
        
            def kaisaras_apokwd(kwdikopoiimeno,d):
                keimeno = ''
                for l in kwdikopoiimeno:
                    thesi = ab.index(l)
                    neathesi = (ab.index(l) - d) % len(ab)
                    keimeno += ab[neathesi]
                return(keimeno)            
        
    
Αντί να προσθέσουμε την απόσταση, την αφαιρούμε.
Το τελικό πρόγραμμα είναι το εξής:
        
            ab = 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ'
            def kaisaras_kwd(keimeno,d):
                kwdikopoiimeno = ''
                for l in keimeno:
                    thesi = ab.index(l)
                    neathesi = (ab.index(l) + d) % len(ab)
                    kwdikopoiimeno += ab[neathesi]
                return(kwdikopoiimeno)

            def kaisaras_apokwd(kwdikopoiimeno,d):
                keimeno = ''
                for l in kwdikopoiimeno:
                    thesi = ab.index(l)
                    neathesi = (ab.index(l) - d) % len(ab)
                    keimeno += ab[neathesi]
                return(keimeno)            

            d = int(input('Δώστε απόσταση κώδικα: '))
            keimeno = input('Δώστε κείμενο για κρυπτογράφηση: ')
            print(kaisaras_kwd(keimeno,d))
            kwdikop = input('Δώστε κώδικα για αποκρυπτογράφηση: ')
            print(kaisaras_apokwd(kwdikop,d))            
        
    
Η κωδικοποίηση με μετατόπιση είναι εύκολο να αποκωδικοποιηθεί ακόμη και αν δεν ξέρουμε την απόσταση. Δείτε το παρακάτω πρόγραμμα που αποκωδικοποιεί το κωδικοποιημένο κείμενο με όλες τις αποστάσεις.
        
            ab = 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ'
            def kaisaras_apokwd(kwdikopoiimeno,d):
                keimeno = ''
                for l in kwdikopoiimeno:
                    thesi = ab.index(l)
                    neathesi = (ab.index(l) - d) % len(ab)
                    keimeno += ab[neathesi]
                return(keimeno)            

            kwdikop = 'ΥΛΦΡΧΟΓΛ'
            for d in range(24):
                print(kaisaras_apokwd(kwdikop,d))
        
    
Το αποτέλεσμα της εκτέλεσης είναι:
     
ΥΛΦΡΧΟΓΛ
ΤΚΥΠΦΞΒΚ
ΣΙΤΟΥΝΑΙ
ΡΘΣΞΤΜΩΘ
ΠΗΡΝΣΛΨΗ
ΟΖΠΜΡΚΧΖ
ΞΕΟΛΠΙΦΕ
ΝΔΞΚΟΘΥΔ
ΜΓΝΙΞΗΤΓ
ΛΒΜΘΝΖΣΒ
ΚΑΛΗΜΕΡΑ
ΙΩΚΖΛΔΠΩ
ΘΨΙΕΚΓΟΨ
ΗΧΘΔΙΒΞΧ
ΖΦΗΓΘΑΝΦ
ΕΥΖΒΗΩΜΥ
ΔΤΕΑΖΨΛΤ
ΓΣΔΩΕΧΚΣ
ΒΡΓΨΔΦΙΡ
ΑΠΒΧΓΥΘΠ
ΩΟΑΦΒΤΗΟ
ΨΞΩΥΑΣΖΞ
ΧΝΨΤΩΡΕΝ
ΦΜΧΣΨΠΔΜ
 

Είναι προφανές πως το αρχικό κείμενο ήταν ΚΑΛΗΜΕΡΑ.

Στην επόμενη ενότητα θα αλλάζουμε την απόσταση σε κάθε γράμμα.

Πώς μπορούμε να κάνουμε την αποκωδικοποίηση σε μία γραμμή;

Θυμηθείτε την κωδικοποίηση στην προηγούμενη ενότητα.
                
                ''.join([ab[(ab.index(l) - d) % len(ab)] for l in keimeno])
                
            

Κώδικας Vigenère

Η κρυπτογράφηση Vigenère αποτελείται από πολλούς αλγόριθμους κρυπτογράφησης του Καίσαρα με διαφορετικές τιμές μετατόπισης για κάθε κωδικοποίηση γράμματος. Οι τιμές της μετατόπισης αποθηκεύονται σε ένα κλειδί που μπορεί να έχει οποιοδήποτε μέγεθος. Ας δούμε πώς διαμορφώνεται η κωδικοποίηση.
        
            ab = 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ'
            def kaisaras_kwd(keimeno,key):
                kwdikopoiimeno = ''
                for (i,l) in enumerate(keimeno):
                    thesi = ab.index(l)
                    d = key[i%len(key)]
                    neathesi = (ab.index(l) + d) % len(ab)
                    kwdikopoiimeno += ab[neathesi]
                return(kwdikopoiimeno)

            key = [1,5,7,9]
            keimeno = input('Δώστε κείμενο για κρυπτογράφηση: ')
            print(kaisaras_kwd(keimeno,key))
        
    
Το i στην εντολή αυτή θα έχει τον αύξοντα αριθμό από το κάθε γράμμα του κειμένου λόγω της enumerate.
Η κωδικοποίηση του συγκεκριμένου γράμματος θα έχει απόσταση που ορίζεται από το κλειδί στη θέση i, οπότε key[i]. Αλλά όταν τελειώνει το κλειδί θα πρέπει να ξαναρχίζει από την αρχή και για αυτό παίρνουμε το υπόλοιπο της διαίρεσης του i με το μήκος του κλειδιού len(key). Η απόσταση γίνεται key[i%len(key)].
Στην περίπτωση αυτή το κλειδί είναι 1,5,7,9, μπορείτε να δείτε στην εκτέλεση του προγράμματος αν όντως το πρώτο γράμμα κωδικοποιείται με απόσταση 1. π.χ.
    
    Δώστε κείμενο για κρυπτογράφηση: ΚΑΛΗΜΕΡΑ
    ΛΖΣΠΝΚΩΚ
    
Το πρώτο γράμμα (Κ) κωδικοποιήθηκε με απόσταση 1 σε Λ, ενώ το δεύτερο (Α) κωδικοποιήθηκε με απόσταση 5 σε Ζ. Το συνολικό πρόγραμμα κωδικοποίησης αποκωδικοποίησης διαμορφώνεται ως εξής:

    ab = 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ'

    def kaisaras_kwd(keimeno,key):
        kwdikopoiimeno = ''
        for (i,l) in enumerate(keimeno):
            thesi = ab.index(l)
            d = key[i%len(key)]
            neathesi = (ab.index(l) + d) % len(ab)
            kwdikopoiimeno += ab[neathesi]
        return(kwdikopoiimeno)

    def kaisaras_apokwd(kwdikopoiimeno,key):
        keimeno = ''
        for (i,l) in enumerate(kwdikopoiimeno):
            thesi = ab.index(l)
            d = key[i%len(key)]
            neathesi = (ab.index(l) - d]) % len(ab)
            keimeno += ab[neathesi]
        return(keimeno)            

    key = [1,5,7,9]
    keimeno = input('Δώστε κείμενο για κρυπτογράφηση: ')
    print(kaisaras_kwd(keimeno,key))
    kwdikop = input('Δώστε κώδικα για αποκρυπτογράφηση: ')
    print(kaisaras_apokwd(kwdikop,key))

Βιβλιογραφία

  1. Ειδικά θέματα στο Υλικό και στα Δίκτυα Υπολογιστών
  2. Κώδικας του Καίσαρα (Wikipedia)
  3. Αλγόριθμος κρυπτογράφησης Vigenère (Wikipedia)