Block-Cipher (Schritt für Schritt)

Einleitung: Permutations-Chiffren

  • Hier ist ein verschlüsselter deutscher Text. Wer kann ihn entschlüsseln?

    UCHBS ABETN ERDWE NURNU SORMT ERTIX
  • Letzte Woche in den Übungen: Caesar-Chiffre – was war da das Prinzip? → Substitution (Buchstaben werden durch andere ersetzt)

  • Obiger Text ist NICHT mit Substitution verschlüsselt. Die Buchstaben sind noch alle da – sie stehen nur an anderen Stellen. Das nennt man Transposition oder Permutation.

  • Zum Entschlüsseln braucht man dieses Mal einen Schlüssel. Das ist ein deutsches Wort mit 5 Buchstaben.

  • Der Schlüssel ist: MACHT.

  1. Das Keyword als Spaltenüberschriften aufschreiben: M A C H T, aber so dass die Buchstaben alphabetisch sortiert sind, also A C H M T.

  2. Darunter den Ciphertext in 5er-Blöcken spaltenweise:

    A  C  H  M  T
    U  C  H  B  S
    A  B  E  T  N
    E  R  D  W  E
    N  U  R  N  U
    S  O  R  M  T
    E  R  T  I  X
  3. Jetzt sortieren wir die Spalten, sodass wieder das Wort MACHT in der ersten Zeile steht.

  4. Dazu müssen wir umordnen: Spalte 1 (aktuell A) kommt an Position 2, Spalte 4 (aktuell M) kommt an Position 1. Nach Umordnung:

    M  A  C  H  T
    B  U  C  H  S
    T  A  B  E  N
    W  E  R  D  E
    N  N  U  R  U
    M  S  O  R  T
    I  E  R  T  X
  5. Jetzt zeilenweise lesen:

    BUCHS TABEN WERDE NNURU MSORT IERTX

Das war mühsam per Hand. Jetzt programmieren wir das – aber andersherum: also die Verschlüsselung.

In unserem Programm geben wir die Permutation, also die Vertauschungen direkt als Zahlen ein (z.B. ‘12304’); die Verarbeitung des Schlüsselworts sparen wir uns. Das Prinzip bleibt gleich!

Phase 1: Problem verstehen

Einstiegsfragen

  • Was bedeutet Verschlüsselung allgemein?
  • Bei der Caesar-Verschlüsselung werden Buchstaben ersetzt. Was passiert hier stattdessen?
  • Schauen wir uns ein Beispiel an. Was würde mit ‘ABC’ passieren, wenn wir die Positionen wie folgt tauschen: Position 0→2, 1→0, 2→1?

Durchspielen am Beispiel

“HELLOWORLD” mit Key “201”. Aus der Aufgabenstellung: Der Permutationsschlüssel “201” bedeutet: Position 0 geht nach 2, Position 1 geht nach 0, Position 2 geht nach 1.

1. String aufteilen:
   HEL | LOW | ORL | D??

2. Was bedeutet Key "201"?
   0 → 2
   1 → 0 
   2 → 1

3. Ersten Block permutieren:
   H E L    Position:  0 1 2
   ↓ ↓ ↓    
   E L H    Neue Pos:  2 0 1

Phase 2: Grundgerüst implementieren

Start mit einfachem Programm

#include <cs50.h>
#include <stdio.h>
#include <string.h>

int main(int argc, string argv[])
{
    if (argc != 2)
    {
        printf("Usage: ./permute text\n");
        return 1;
    }

    string text = argv[1];
    printf("Input text: %s\n", text);
    
    return 0;
}

Fragen:

  • Was brauchen wir als nächstes? Wie können wir den Text in Blöcke aufteilen?
  • Welche Konstante sollten wir definieren?
  • Wie können wir durch den Text in 3er-Schritten iterieren?

Pseudocode: Blockweise iterieren

SIZE = 3

// Einstieg: Erstmal nur Blocks ausgeben
FÜR Position = 0 BIS Textlänge SCHRITTWEITE SIZE:
    AUSGABE "Block beginnt bei Position", Position
    FÜR j = 0 BIS SIZE:
        AUSGABE Text[Position + j]
    AUSGABE " | "

Erste Erweiterung

#define BLOCK_SIZE 3

int main(int argc, string argv[])
{
    if (argc != 2)
    {
        printf("Usage: ./permute text\n");
        return 1;
    }
    
    printf("Blocks: ");
    for (int i = 0; i < strlen(text); i += BLOCK_SIZE)
    {
        // Jeden Block ausgeben
        for (int j = 0; j < BLOCK_SIZE; j++)
        {
            printf("%c", text[i + j]);
        }
        printf(" | ");
    }
    printf("\n");
}

Soweit so gut.

Nehmen wir an, wir hätten eine Funktion, die einen Block verarbeitet: VERARBEITE Block.

Dann wäre es gut, wenn wir beim Iterieren etwas hätten, das den aktuellen Block enthält, also so:

// Verbesserung: Mit Array für jeden Block
FÜR Position = 0 BIS Textlänge SCHRITTWEITE SIZE:
    ERSTELLE Block[SIZE]  // Temporäres Array für aktuellen Block
    FÜR j = 0 BIS SIZE:
        Block[j] = Text[Position + j]
    
    VERARBEITE Block
    AUSGABE Block

Die Block-Extraktion lagern wir am besten in eine Funktion aus.

Phase 3: Block-Extraktion

Aufgabe

Schreiben Sie eine Funktion, die einen einzelnen Block in ein char-Array kopiert:

  • Der Block beginnt bei Position start im Text.
  • Er soll BLOCK_SIZE Zeichen lang sein.
  • Verwenden Sie ein char-Array als Parameter für den erstellten Block.
  • Die Funktion gibt nichts zurück.

Besprechung der Lösung

void extract_block(string text, int start, char block[])
{
    for (int j = 0; j < BLOCK_SIZE; j++)
    {
        block[j] = text[start + j];
    }
}

Diskussionsfragen:

  • Warum verwenden wir char[] statt string?
  • Was könnte schiefgehen, wenn der Text zu kurz ist?
  • Wie können wir das prüfen?

Phase 4: Permutation implementieren

Implementierung

// Einfache Variante: Hart kodiert für Key "201"
EINGABE: Block[BLOCK_SIZE]

ERSTELLE Temp[BLOCK_SIZE]     // Warum brauchen wir das?
Temp[2] = Block[0]   // 0 geht nach 2
Temp[0] = Block[1]   // 1 geht nach 0
Temp[1] = Block[2]   // 2 geht nach 1

KOPIERE Temp zurück nach Block

oder in C:

// Erstmal mit festem Key "201" testen
void permute_block(char block[])
{
    char temp[BLOCK_SIZE];
    
    // Position 0 geht nach 2
    temp[2] = block[0];
    
    // Position 1 geht nach 0
    temp[0] = block[1];
    
    // Position 2 geht nach 1
    temp[1] = block[2];
    
    // Zurückkopieren
    for (int i = 0; i < BLOCK_SIZE; i++)
    {
        block[i] = temp[i];
    }
}

Fragen zur Verbesserung:

  • Was ist problematisch an diesem Code?
  • Wie könnten wir den Key flexibel machen?
  • Warum brauchen wir temp[]?

main() für Phase 4 (zum Testen)

#include <cs50.h>
#include <stdio.h>
#include <string.h>

#define BLOCK_SIZE 3

void extract_block(string text, int start, char block[])
{
    for (int j = 0; j < BLOCK_SIZE; j++)
    {
        block[j] = text[start + j];
    }
}

void permute_block(char block[])
{
    char temp[BLOCK_SIZE];

    // Position 0 geht nach 2
    temp[2] = block[0];

    // Position 1 geht nach 0
    temp[0] = block[1];

    // Position 2 geht nach 1
    temp[1] = block[2];

    // Zurückkopieren
    for (int i = 0; i < BLOCK_SIZE; i++)
    {
        block[i] = temp[i];
    }
}

int main(int argc, string argv[])
{
    if (argc != 2)
    {
        printf("Usage: ./permute text\n");
        return 1;
    }

    string text = argv[1];

    // Text blockweise verarbeiten
    printf("Output: ");
    for (int i = 0; i < strlen(text); i += BLOCK_SIZE)
    {
        char block[BLOCK_SIZE];
        extract_block(text, i, block);
        permute_block(block);

        // Block ausgeben
        for (int j = 0; j < BLOCK_SIZE; j++)
        {
            printf("%c", block[j]);
        }
    }
    printf("\n");

    return 0;
}

Test:

./permute HELLOWORLD

Erwartete Ausgabe: ELHOLWLRDO (jeder 3er-Block wird mit “201” permutiert)

Phase 4.5: Flexibler Key mit String

Problem

Der hartkodierte Key “201” ist unpraktisch. Wir wollen den Key als Parameter übergeben.

Erste Lösung: Key als String

Wir erweitern die Funktion, damit sie einen Key-String entgegennimmt:

void permute_block(char block[], string key_str)

Das Problem: Zeichen vs. Zahlen

Der Key ist ein String, also z.B. “201”. Das bedeutet:

  • key_str[0] ist das Zeichen '2' (nicht die Zahl 2!)
  • key_str[1] ist das Zeichen '0'
  • key_str[2] ist das Zeichen '1'

Wir brauchen aber die Zahlen 2, 0, 1 als Array-Indizes.

Lösung: ASCII-Arithmetik

int pos = key_str[i] - '0';

Das Zeichen ‘0’ hat den ASCII-Code 48, ‘1’ hat 49, ‘2’ hat 50, usw.

  • '2' - '0' = 50 - 48 = 2
  • '0' - '0' = 48 - 48 = 0
  • '1' - '0' = 49 - 48 = 1

Implementation

Pseudocode:

EINGABE: Block[BLOCK_SIZE], KeyString

ERSTELLE Temp[BLOCK_SIZE]
FÜR i = 0 BIS BLOCK_SIZE:
    Position = KeyString[i] - '0'        // Zeichen zu Zahl konvertieren!
    Temp[Position] = Block[i]            // Position i geht nach Position

KOPIERE Temp zurück nach Block

In C:

void permute_block(char block[], string key_str)
{
    char temp[BLOCK_SIZE];
    for (int i = 0; i < BLOCK_SIZE; i++)
    {
        int pos = key_str[i] - '0';      // Zeichen zu Zahl konvertieren
        temp[pos] = block[i];
    }
    for (int i = 0; i < BLOCK_SIZE; i++)
    {
        block[i] = temp[i];
    }
}

Verständnisfragen

  • Warum subtrahieren wir ‘0’ von key_str[i]?
  • Was würde passieren, wenn wir key_str[i] direkt als Index verwenden?
  • Welche Werte kann pos annehmen bei einem gültigen Key?

main() für Phase 4.5 (zum Testen)

#include <cs50.h>
#include <stdio.h>
#include <string.h>

#define BLOCK_SIZE 3

void extract_block(string text, int start, char block[])
{
    for (int j = 0; j < BLOCK_SIZE; j++)
    {
        block[j] = text[start + j];
    }
}

void permute_block(char block[], string key_str)
{
    char temp[BLOCK_SIZE];
    for (int i = 0; i < BLOCK_SIZE; i++)
    {
        int pos = key_str[i] - '0';
        temp[pos] = block[i];
    }
    for (int i = 0; i < BLOCK_SIZE; i++)
    {
        block[i] = temp[i];
    }
}

int main(int argc, string argv[])
{
    if (argc != 3)
    {
        printf("Usage: ./permute key text\n");
        return 1;
    }

    string key_str = argv[1];
    string text = argv[2];

    // Text blockweise verarbeiten
    printf("Output: ");
    for (int i = 0; i < strlen(text); i += BLOCK_SIZE)
    {
        char block[BLOCK_SIZE];
        extract_block(text, i, block);
        permute_block(block, key_str);

        // Block ausgeben
        for (int j = 0; j < BLOCK_SIZE; j++)
        {
            printf("%c", block[j]);
        }
    }
    printf("\n");

    return 0;
}

Test:

./permute 201 HELLOWORLD
./permute 012 HELLOWORLD
./permute 120 ABC

Beobachtung: Noch keine Validierung - ungültige Keys wie “999” oder “abc” führen zu unerwartetem Verhalten!

Phase 5: Key-Validierung

Analyse

  • Welche Bedingungen muss ein gültiger Key erfüllen?
  • Was passiert bei ungültigen Keys wie ‘011’ oder ‘234’?

Implementation der Validierung

// Nur Grundprüfung
WENN Keylänge ≠ BLOCK_SIZE:
    FEHLER "Key muss Länge 3 haben"

FÜR JEDE Ziffer IN Key:
    WENN Ziffer < 0 ODER Ziffer ≥ BLOCK_SIZE:
        FEHLER "Ungültige Ziffer im Key"

Das reicht aber noch nicht.

011 ist keine gültige Permutation, 510 auch nicht.

// Weitere Prüfungen
ERSTELLE Used[BLOCK_SIZE] = {0,0,0}  // Zähler für jede Position

FÜR JEDE Ziffer IN Key:
    WENN Ziffer < 0 ODER Ziffer ≥ BLOCK_SIZE:
        FEHLER "Ungültige Ziffer"
    Used[Ziffer]++

FÜR i = 0 BIS BLOCK_SIZE:
    WENN Used[i] ≠ 1:
        FEHLER "Jede Position muss genau einmal vorkommen"

Also in C:

bool is_valid_key(string key_str)
{
    // Längenkontrolle
    if (strlen(key_str) != BLOCK_SIZE)
    {
        return false;
    }

    // Array für Positionszählung
    int used[BLOCK_SIZE] = {0};

    // Jeden Key-Character prüfen
    for (int i = 0; i < BLOCK_SIZE; i++)
    {
        // Ist es eine gültige Ziffer?
        if (key_str[i] < '0' || key_str[i] >= '0' + BLOCK_SIZE)
        {
            return false;
        }

        // Position markieren - wieder '0' abziehen!
        int pos = key_str[i] - '0';
        used[pos]++;
    }

    // Jede Position genau einmal?
    for (int i = 0; i < BLOCK_SIZE; i++)
    {
        if (used[i] != 1)
        {
            return false;
        }
    }

    return true;
}

Verständnisfragen

  • Was bedeutet used[pos]++?
  • Wie prüfen wir, ob jede Position genau einmal vorkommt?
  • Warum funktioniert key_str[i] < '0' als Prüfung?

main() für Phase 5 (zum Testen)

#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>

#define BLOCK_SIZE 3

void extract_block(string text, int start, char block[])
{
    for (int j = 0; j < BLOCK_SIZE; j++)
    {
        block[j] = text[start + j];
    }
}

void permute_block(char block[], string key_str)
{
    char temp[BLOCK_SIZE];
    for (int i = 0; i < BLOCK_SIZE; i++)
    {
        int pos = key_str[i] - '0';
        temp[pos] = block[i];
    }
    for (int i = 0; i < BLOCK_SIZE; i++)
    {
        block[i] = temp[i];
    }
}

bool is_valid_key(string key_str)
{
    if (strlen(key_str) != BLOCK_SIZE)
    {
        return false;
    }

    int used[BLOCK_SIZE] = {0};

    for (int i = 0; i < BLOCK_SIZE; i++)
    {
        if (key_str[i] < '0' || key_str[i] >= '0' + BLOCK_SIZE)
        {
            return false;
        }

        int pos = key_str[i] - '0';
        used[pos]++;
    }

    for (int i = 0; i < BLOCK_SIZE; i++)
    {
        if (used[i] != 1)
        {
            return false;
        }
    }

    return true;
}

int main(int argc, string argv[])
{
    if (argc != 3)
    {
        printf("Usage: ./permute key text\n");
        return 1;
    }

    string key_str = argv[1];
    string text = argv[2];

    // Key validieren
    if (!is_valid_key(key_str))
    {
        printf("Invalid key\n");
        return 1;
    }

    // Text-Länge prüfen
    if (strlen(text) % BLOCK_SIZE != 0)
    {
        printf("Text length must be multiple of %i\n", BLOCK_SIZE);
        return 1;
    }

    // Text blockweise verarbeiten
    printf("Output: ");
    for (int i = 0; i < strlen(text); i += BLOCK_SIZE)
    {
        char block[BLOCK_SIZE];
        extract_block(text, i, block);
        permute_block(block, key_str);

        for (int j = 0; j < BLOCK_SIZE; j++)
        {
            printf("%c", block[j]);
        }
    }
    printf("\n");

    return 0;
}

Tests:

./permute 201 HELLOWORLD    # Funktioniert
./permute 012 ABCDEFGHI     # Funktioniert
./permute 999 ABC           # Fehlermeldung: Invalid key
./permute 011 ABC           # Fehlermeldung: Invalid key (doppelte Position)
./permute 201 HELL          # Fehlermeldung: Text length must be multiple of 3

Beobachtung

Fällt etwas auf? Wir ziehen hier schon wieder '0' ab (in is_valid_key()) - genau wie in permute_block() (Phase 4.5). Das wird gleich noch wichtig…

Phase 5.5: Code Smell erkennen und beheben

Das Problem: Code-Duplikation

Schauen wir uns an, wo wir überall '0' abziehen:

  1. In permute_block() (Phase 4.5):

    int pos = key_str[i] - '0';
  2. In is_valid_key() (Phase 5):

    int pos = key_str[i] - '0';
  3. Gleich in main() werden wir das auch noch brauchen:

    for (int i = 0; i < BLOCK_SIZE; i++)
    {
        key[i] = key_str[i] - '0';  // Schon wieder!
    }

Das ist ein “Code Smell” - ein Hinweis auf schlechtes Design!

Warum ist das problematisch?

  • Fehleranfälligkeit: Wenn wir einen Fehler in der Konversion machen, müssen wir ihn an allen Stellen fixen
  • Wartbarkeit: Änderungen müssen überall nachgezogen werden
  • Verständlichkeit: Der gleiche Code steht mehrfach da

Prinzip: Don’t Repeat Yourself (DRY)

Lösung: parse_key() Funktion

Wir lagern die Konversion in eine eigene Funktion aus:

void parse_key(string key_str, int key[])
{
    for (int i = 0; i < BLOCK_SIZE; i++)
    {
        key[i] = key_str[i] - '0';
    }
}

Wichtig: Arrays als Parameter

Beobachtung: Die Funktion gibt nichts zurück (void), verändert aber trotzdem das key[] Array!

Grund: Wenn wir ein Array an eine Funktion übergeben, können wir dessen Inhalt verändern - und diese Änderungen sind auch außerhalb der Funktion sichtbar. Das ist anders als bei normalen Variablen!

Beispiel:

int main(void)
{
    string key_str = "201";
    int key[BLOCK_SIZE];

    parse_key(key_str, key);  // Funktion füllt das Array

    // Jetzt können wir key[] verwenden:
    printf("%i %i %i\n", key[0], key[1], key[2]);  // Ausgabe: 2 0 1
}

Vorteile

Jetzt können wir die anderen Funktionen vereinfachen:

  • is_valid_key() bekommt int key[] statt string key_str
  • permute_block() bekommt int key[] statt string key_str
  • Kein '0' abziehen mehr in diesen Funktionen!

Ablauf in main():

1. Key-String einlesen (z.B. "201")
2. parse_key() aufrufen → int[] erstellen
3. is_valid_key() mit int[] aufrufen
4. permute_block() mit int[] aufrufen

Phase 6: Refactoring und Integration

Nach der Einführung von parse_key() können wir jetzt unsere Funktionen vereinfachen.

Schritt 1: is_valid_key() anpassen

Vorher (mit string):

bool is_valid_key(string key_str)
{
    if (strlen(key_str) != BLOCK_SIZE)
    {
        return false;
    }

    int used[BLOCK_SIZE] = {0};

    for (int i = 0; i < BLOCK_SIZE; i++)
    {
        if (key_str[i] < '0' || key_str[i] >= '0' + BLOCK_SIZE)  // Kompliziert!
        {
            return false;
        }

        int pos = key_str[i] - '0';  // '0' abziehen!
        used[pos]++;
    }

    for (int i = 0; i < BLOCK_SIZE; i++)
    {
        if (used[i] != 1)
        {
            return false;
        }
    }

    return true;
}

Nachher (mit int-Array - viel einfacher!):

bool is_valid_key(int key[])
{
    int used[BLOCK_SIZE] = {0};

    for (int i = 0; i < BLOCK_SIZE; i++)
    {
        // Ist es eine gültige Ziffer? Viel einfacher zu lesen!
        if (key[i] < 0 || key[i] >= BLOCK_SIZE)
        {
            return false;
        }

        // Kein '0' abziehen mehr nötig!
        used[key[i]]++;
    }

    // Jede Position genau einmal?
    for (int i = 0; i < BLOCK_SIZE; i++)
    {
        if (used[i] != 1)
        {
            return false;
        }
    }

    return true;
}

Beachte: Die Längenkontrolle entfällt - die machen wir jetzt in parse_key() oder in main()!

Schritt 2: permute_block() anpassen

Vorher (mit string):

void permute_block(char block[], string key_str)
{
    char temp[BLOCK_SIZE];
    for (int i = 0; i < BLOCK_SIZE; i++)
    {
        int pos = key_str[i] - '0';  // '0' abziehen!
        temp[pos] = block[i];
    }
    for (int i = 0; i < BLOCK_SIZE; i++)
    {
        block[i] = temp[i];
    }
}

Nachher (mit int-Array):

void permute_block(char block[], int key[])
{
    char temp[BLOCK_SIZE];
    for (int i = 0; i < BLOCK_SIZE; i++)
    {
        temp[key[i]] = block[i];  // Direkt verwenden - kein '0' abziehen!
    }
    for (int i = 0; i < BLOCK_SIZE; i++)
    {
        block[i] = temp[i];
    }
}

Schritt 3: main() komplett überarbeiten

Jetzt fügen wir alles zusammen. Wichtig ist die Reihenfolge:

int main(int argc, string argv[])
{
    if (argc != 3)
    {
        printf("Usage: ./permute key text\n");
        return 1;
    }

    string key_str = argv[1];
    string text = argv[2];

    // 1. Prüfen: Ist der Key-String die richtige Länge?
    if (strlen(key_str) != BLOCK_SIZE)
    {
        printf("Key must have length %i\n", BLOCK_SIZE);
        return 1;
    }

    // 2. Key parsen (string → int-Array)
    int key[BLOCK_SIZE];
    parse_key(key_str, key);

    // 3. Validieren (mit int-Array!)
    if (!is_valid_key(key))
    {
        printf("Invalid key\n");
        return 1;
    }

    // 4. Text-Länge prüfen
    if (strlen(text) % BLOCK_SIZE != 0)
    {
        printf("Text length must be multiple of %i\n", BLOCK_SIZE);
        return 1;
    }

    // 5. Text blockweise verarbeiten
    printf("Output: ");
    for (int i = 0; i < strlen(text); i += BLOCK_SIZE)
    {
        char block[BLOCK_SIZE];
        extract_block(text, i, block);
        permute_block(block, key);  // Mit int-Array!

        // Block ausgeben
        for (int j = 0; j < BLOCK_SIZE; j++)
        {
            printf("%c", block[j]);
        }
    }
    printf("\n");

    return 0;
}

Komplettes Programm für Phase 6 (zum Testen)

#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>

#define BLOCK_SIZE 3

// Block aus Text extrahieren
void extract_block(string text, int start, char block[])
{
    for (int j = 0; j < BLOCK_SIZE; j++)
    {
        block[j] = text[start + j];
    }
}

// Key-String in int-Array konvertieren
void parse_key(string key_str, int key[])
{
    for (int i = 0; i < BLOCK_SIZE; i++)
    {
        key[i] = key_str[i] - '0';
    }
}

// Key validieren (nimmt jetzt int-Array!)
bool is_valid_key(int key[])
{
    int used[BLOCK_SIZE] = {0};

    for (int i = 0; i < BLOCK_SIZE; i++)
    {
        if (key[i] < 0 || key[i] >= BLOCK_SIZE)
        {
            return false;
        }

        used[key[i]]++;
    }

    for (int i = 0; i < BLOCK_SIZE; i++)
    {
        if (used[i] != 1)
        {
            return false;
        }
    }

    return true;
}

// Block permutieren (nimmt jetzt int-Array!)
void permute_block(char block[], int key[])
{
    char temp[BLOCK_SIZE];
    for (int i = 0; i < BLOCK_SIZE; i++)
    {
        temp[key[i]] = block[i];
    }
    for (int i = 0; i < BLOCK_SIZE; i++)
    {
        block[i] = temp[i];
    }
}

int main(int argc, string argv[])
{
    if (argc != 3)
    {
        printf("Usage: ./permute key text\n");
        return 1;
    }

    string key_str = argv[1];
    string text = argv[2];

    // 1. Prüfen: Ist der Key-String die richtige Länge?
    if (strlen(key_str) != BLOCK_SIZE)
    {
        printf("Key must have length %i\n", BLOCK_SIZE);
        return 1;
    }

    // 2. Key parsen (string → int-Array)
    int key[BLOCK_SIZE];
    parse_key(key_str, key);

    // 3. Validieren (mit int-Array!)
    if (!is_valid_key(key))
    {
        printf("Invalid key\n");
        return 1;
    }

    // 4. Text-Länge prüfen
    if (strlen(text) % BLOCK_SIZE != 0)
    {
        printf("Text length must be multiple of %i\n", BLOCK_SIZE);
        return 1;
    }

    // 5. Text blockweise verarbeiten
    printf("Output: ");
    for (int i = 0; i < strlen(text); i += BLOCK_SIZE)
    {
        char block[BLOCK_SIZE];
        extract_block(text, i, block);
        permute_block(block, key);

        for (int j = 0; j < BLOCK_SIZE; j++)
        {
            printf("%c", block[j]);
        }
    }
    printf("\n");

    return 0;
}

Tests:

./permute 201 HELLOWORLD    # Funktioniert
./permute 012 ABCDEFGHI     # Funktioniert
./permute 120 ABC           # Funktioniert
./permute 999 ABC           # Fehlermeldung: Invalid key
./permute 011 ABC           # Fehlermeldung: Invalid key
./permute 20 ABC            # Fehlermeldung: Key must have length 3
./permute 201 HELL          # Fehlermeldung: Text length must be multiple of 3

Ideen für weitere Aufgaben zum Üben

  1. Wie würden wir die Entschlüsselung implementieren?
  2. Was müsste sich ändern für variable Blocklängen? (erst wenn wir dynamische Speicherverwaltung behandelt haben)
  3. Wie könnten wir mit Texten umgehen, deren Länge kein Vielfaches von BLOCK_SIZE ist?

Debugging-Tipps

  • Nach kleinen Änderungen Kompilieren, um Fehlersuche zu beschleunigen
  • Printf nach jedem Schritt für Zwischenergebnisse
  • Kleine Testfälle wie “ABC” mit Key “201”
  • Systematisches Testen der Randfälle:
    • Zu kurzer Text
    • Ungültige Keys
    • Spezielle Zeichen im Text

Testfälle

Textlänge nicht teilbar durch 3

./permute 201 HELL

Ungültige Keys

./permute 011 HELLOWORLD  // Ziffer doppelt
./permute 234 HELLOWORLD  // Ziffer zu groß
./permute 21  HELLOWORLD  // Key zu kurz`

Spezialfälle

./permute 201 ""         // Leerer Text
./permute 201 "ABC"      // Minimale Länge
./permute 201 "ABCDEF"   // Mehrere Blöcke`