# Cäsar

Quelle: https://dev.inf.zone/exercises/04/caesar/

![Cäsar-Verschlüsselung](https://dev.inf.zone/exercises/04/caesar/cipher.jpg)

## Aufgabe

**Auf einen Blick**

- **Was:** Ein Programm `caesar`, das Text mit der Cäsar-Verschlüsselung verschlüsselt.
- **Datei:** `caesar.c` in einem Ordner `caesar`.
- **Aufruf:** `./caesar 13` – der Schlüssel ist das einzige Kommandozeilenargument, eine nicht-negative ganze Zahl.
- **Ein- und Ausgabe:** `plaintext:  HELLO` → `ciphertext: URYYB`
- **Sonderfälle:** kein, mehr als ein oder ein nicht-numerisches Argument → Fehlermeldung, Rückgabewert `1`. Groß- und Kleinschreibung bleibt erhalten, andere Zeichen bleiben unverändert. Der Schlüssel kann größer als 26 sein.

### So funktioniert die Cäsar-Verschlüsselung

Jeder Buchstabe wird um eine feste Zahl von Stellen im Alphabet verschoben; nach Z geht es bei A weiter. Diese Zahl ist der _Schlüssel_. Unverschlüsselter Text heißt _Plaintext_ (Klartext), verschlüsselter Text _Ciphertext_. Mit dem Schlüssel \(1\) wird aus `HELLO` der Ciphertext `IFMMP`:

| Plaintext    | `H`   | `E`   | `L`   | `L`   | `O`   |
| ------------ | ----- | ----- | ----- | ----- | ----- |
| + Schlüssel  | \(1\)   | \(1\)   | \(1\)   | \(1\)   | \(1\)   |
| = Ciphertext | `I`   | `F`   | `M`   | `M`   | `P`   |

Zum Rechnen nummeriert man die Buchstaben: A (oder a) ist \(0\), B ist \(1\), …, Z ist \(25\). Ist \(p_i\) die Nummer des \(i\)-ten Buchstabens im Plaintext und \(k\) der Schlüssel, dann ist die Nummer des \(i\)-ten Buchstabens im Ciphertext

\(c_i = (p_i + k)\) % 26

Dabei bedeutet `% 26` „Rest bei der Division durch 26“; so geht es nach Z wieder bei A los. Beispiel `Hey` mit dem Schlüssel \(3\):

| Plaintext                | `H`  | `e`  | `y`  |
| ------------------------ | ---- | ---- | ---- |
| Nummer im Alphabet       | \(7\)  | \(4\)  | \(24\) |
| + Schlüssel \(3\)          | \(10\) | \(7\)  | \(27\) |
| % 26                     | \(10\) | \(7\)  | \(1\)  |
| = Ciphertext             | `K`  | `h`  | `b`  |

Warum das Verfahren nach Cäsar heißt, steht am Ende der Seite unter „Zum Weiterlesen“.

## Demo

[Terminal-Aufzeichnung ansehen](https://asciinema.org/a/JnlhDTjc264WfGSoNxc0hsjEY)

## Spezifikation

Entwerfen und implementieren Sie ein Programm, `caesar`, das Nachrichten mit der Cäsar-Verschlüsselung verschlüsselt.

-   Implementieren Sie Ihr Programm in C in einer Datei namens `caesar.c` in einem Ordner namens `caesar`.
-   Ihr Programm muss ein einzelnes Kommandozeilenargument akzeptieren, eine nicht-negative Ganzzahl. Nennen wir diese im weiteren Verlauf der Einfachheit halber \(k\).
-   Wenn Ihr Programm ohne Kommandozeilenargumente oder mit mehr als einem Kommandozeilenargument ausgeführt wird, sollte Ihr Programm eine Fehlermeldung Ihrer Wahl (mit `printf`) ausgeben und sofort einen Wert von `1` aus `main` zurückgeben (was normalerweise einen Fehler signalisiert).
-   Wenn eines der Zeichen des Kommandozeilenarguments keine Dezimalziffer ist, sollte Ihr Programm die Nachricht `Usage: ./caesar key` ausgeben und einen Wert von `1` aus `main` zurückgeben.
-   Gehen Sie nicht davon aus, dass \(k\) kleiner oder gleich 26 ist. Ihr Programm sollte für alle nicht-negativen Ganzzahlwerte von \(k\) funktionieren, die kleiner als \(2^{31} - 26\) sind. Das heißt, Sie müssen sich keine Sorgen machen, wenn Ihr Programm irgendwann nicht mehr funktioniert, wenn der Benutzer einen Wert für \(k\) wählt, der zu groß oder fast zu groß ist, um in einen `int` zu passen. (Erinnern Sie sich daran, dass ein `int` überlaufen kann.) Ansonsten gilt aber: Auch wenn \(k\) größer als 26 ist, sollten in das Programm eingegebene alphabetische Zeichen auch bei der Ausgabe alphabetische Zeichen bleiben. Wenn \(k\) beispielsweise \(27\) ist, sollte `A` nicht zu `\` werden, auch wenn `\`, laut [asciitable.com](https://www.asciitable.com/), 27 Positionen von `A` im ASCII-Code entfernt ist; `A` sollte zu `B` werden, da `B` 27 Positionen von `A` entfernt ist, vorausgesetzt, Sie laufen von `Z` zu `A` über.
-   Ihr Programm muss `plaintext:` (mit zwei Leerzeichen, aber ohne Zeilenumbruch) ausgeben und den Benutzer dann einen `string` mit dem Plaintext eingeben lassen (mithilfe von `get_string`).
-   Ihr Programm muss `ciphertext:` (mit einem Leerzeichen, aber ohne Zeilenumbruch) gefolgt vom entsprechenden Ciphertext des Plaintexts ausgeben, wobei jedes alphabetische Zeichen im Plaintext um \(k\) Positionen „rotiert“ wird; nicht-alphabetische Zeichen sollten unverändert ausgegeben werden.
-   Ihr Programm muss die Groß-/Kleinschreibung beibehalten: Großbuchstaben müssen, trotz Rotation, Großbuchstaben bleiben; dasselbe gilt für Kleinbuchstaben.
-   Nachdem der Ciphertext ausgegeben wurde, sollten Sie einen Zeilenumbruch ausgeben. Ihr Programm sollte dann durch Rückgabe von `0` aus `main` beendet werden.

## Hilfestellung

**Ordner und Datei anlegen**

Legen Sie einen Ordner `caesar` mit der Datei `caesar.c` an, wie bei [Scrabble](https://dev.inf.zone/exercises/03/scrabble/#schritt-für-schritt) beschrieben. Mit `cd` allein kommen Sie vorher zurück in Ihr Homeverzeichnis.

Wie soll man anfangen? Nähern wir uns dieser Aufgabe Schritt für Schritt.

### Pseudocode

Schreiben Sie zuerst eine `main`-Funktion in `caesar.c`, die das Programm nur mit Pseudocode skizziert, auch wenn Sie (noch!) nicht sicher sind, wie Sie es mit richtigem Code implementieren sollen.

**Tipp**

Es gibt mehr als eine Möglichkeit, dies zu tun, hier ist nur eine davon!

```c
int main(int argc, string argv[])
{
    // Make sure program was run with just one command-line argument

    // Make sure every character in argv[1] is a digit

    // Convert argv[1] from a `string` to an `int`

    // Prompt user for plaintext

    // For each character in the plaintext:

        // Rotate the character if it's a letter
}
```

Es ist in Ordnung, Ihren eigenen Pseudocode zu bearbeiten, nachdem Sie diesen hier gesehen haben, aber kopieren Sie ihn nicht einfach in Ihren eigenen!

### Kommandozeilenargumente zählen

Wie auch immer Ihr Pseudocode aussieht, lassen Sie uns zunächst nur den C-Code schreiben, der prüft, ob das Programm mit einem einzigen Kommandozeilenargument ausgeführt wurde.

Hierzu müssen Sie Änderungen an der `main`-Funktion vornehmen. Diese sollte `"Usage: ./caesar key\n"` ausgeben und dann durch die Rückgabe von `1` das Programm beenden, wenn der Benutzer keine Kommandozeilenargumente oder zwei oder mehr angibt. Wenn der Benutzer genau ein Kommandozeilenargument angibt, sollte das Programm erst einmal nichts ausgeben und einfach `0` zurückgeben. Das Programm sollte sich also wie im Folgenden gezeigt verhalten:

```
$ ./caesar
Usage: ./caesar key
```

```
$ ./caesar 1 2 3
Usage: ./caesar key
```

```
$ ./caesar 1
```

**Tipps**

-   Erinnern Sie sich daran, dass Sie mit `printf` etwas ausgeben können.
-   Erinnern Sie sich daran, dass eine Funktion mit `return` einen Wert zurückgeben kann.
-   Erinnern Sie sich daran, dass `argc` die Anzahl der Kommandozeilenargumente enthält, die an ein Programm übergeben werden, und auch den Namen des Programms selbst.

### Überprüfen des Schlüssels

Jetzt, da Ihr Programm (hoffentlich!) die Eingaben wie vorgeschrieben akzeptiert, ist es Zeit für den nächsten Schritt.

Fügen Sie unterhalb von `main` eine Funktion hinzu, die z.B. `only_digits` heißt, die einen `string` als Argument nimmt und `true` zurückgibt, wenn dieser `string` nur Ziffern, `0` bis `9`, enthält, andernfalls gibt sie `false` zurück. Stellen Sie sicher, dass Sie den Prototyp der Funktion auch über `main` einfügen.

**Tipps**

-   Wahrscheinlich brauchen Sie einen Prototyp wie diesen:

    ```c
    bool only_digits(string s);
    ```

    Und stellen Sie sicher, dass Sie `cs50.h` in Ihre Datei einbinden, so dass der Compiler `string` (und `bool`) erkennt.

-   Erinnern Sie sich daran, dass ein `string` einfach ein Array ist, das aus `char` besteht.
-   Erinnern Sie sich, dass `strlen`, deklariert in `string.h`, die Länge von einem `string` zurückgibt.
-   Sie könnten zudem `isdigit`, deklariert in `ctype.h`, als hilfreich empfinden, siehe [manual.cs50.io](https://manual.cs50.io/3/isdigit). Aber beachten Sie, dass diese immer nur ein `char` auf einmal prüft!

Dann passen Sie `main` so an, dass diese `only_digits` auf `argv[1]` aufruft. Wenn diese Funktion `false` zurückgibt, dann sollte `main` `"Usage: ./caesar key\n"` ausgeben und `1` zurückgeben. Andernfalls sollte `main` weiterhin erst einmal nur `0` zurückgeben. Das Programm sollte sich also wie folgt verhalten:

```
$ ./caesar 42
```

```
$ ./caesar banana
Usage: ./caesar key
```

### Verwenden des Schlüssels

Ergänzen Sie nun `main` so, dass es `argv[1]` in einen `int` umwandelt. Hierbei könnte Ihnen etwa `atoi` behilflich sein, das gemäß [Handbuch](https://manual.cs50.io/3/atoi) in `stdlib.h` deklariert ist. Benutzen Sie dann wie gewohnt `get_string`, um den Benutzer mit `"plaintext:  "` (zwei Leerzeichen) nach dem Plaintext zu fragen.

Als nächstes implementieren Sie eine Funktion, die z.B. `rotate` heißt, und als Eingabe einen `char` und einen `int` entgegennimmt. Der `int` legt dabei fest, um wie viele Positionen der übergebene `char` rotiert wird. Aber nur, wenn es ein Buchstabe ist! Wenn der `char` kein Buchstabe ist, sollte die Funktion stattdessen den gleichen `char` unverändert zurückgeben. Zusätzlich muss, falls erforderlich, der Überlauf von `Z` nach `A` (und von `z` nach `a`) behandelt werden.

**Tipps**

-   Wahrscheinlich brauchen Sie einen Prototyp wie diesen:

    ```c
    char rotate(char c, int n);
    ```

    Ein Funktionsaufruf wie

    ```c
    rotate('A', 1)
    ```

    oder auch wie dieser

    ```c
    rotate('A', 27)
    ```

    sollte `'B'` zurückgeben. Und ein Funktionsaufruf wie

    ```c
    rotate('!', 13)
    ```

    sollte `'!'` zurückgeben.

-   Erinnern Sie sich daran, dass Sie mit `(int)` explizit einen `char` in einen `int` und mit `(char)` einen `int` in einen `char` "casten" können. Sie können dies auch implizit tun, indem Sie das eine einfach als das andere behandeln (z.B. indem Sie eine Subtraktion mit einem `char` durchführen).
-   Vermutlich ist es sinnvoll, den ASCII-Wert von `'A'` von allen Großbuchstaben zu subtrahieren, um `'A'` als `0`, `'B'` als `1` usw. zu behandeln, während Sie arithmetische Berechnungen durchführen. Wenn Sie fertig sind, addieren Sie einfach wieder das `'A'`. Dasselbe Vorgehen funktioniert natürlich auch mit den Kleinbuchstaben.
-   Sie könnten einige andere Funktionen, die in `ctype.h` deklariert sind, als hilfreich empfinden, siehe [manual.cs50.io](https://manual.cs50.io/).
-   Erinnern Sie sich, wie hilfreich `%` (modulo) sein kann, wenn Sie arithmetisch von einem Wert wie `25` zu `0` "überlaufen" wollen.

Erweitern Sie `main` abschließend so, dass es `"ciphertext: "` ausgibt und dann über jedes `char` im Klartext des Benutzers iteriert, für jedes `rotate` aufruft und den Rückgabewert ausgibt.

**Tipps**

-   Erinnern Sie sich, dass `printf` ein `char` mit `%c` ausgeben kann.
-   Wenn Sie beim Aufruf von `printf` überhaupt keine Ausgabe sehen, liegt das wahrscheinlich daran, dass Sie Zeichen außerhalb des gültigen ASCII-Bereichs von 0 bis 127 ausgeben. Versuchen Sie, Zeichen vorübergehend als Zahlen auszugeben (mit `%i` statt `%c`), um zu sehen, welche Werte von Ihrer Funktion zurückgegeben werden!

## Testen

### Korrektheit

Führen Sie in Ihrem Terminal den folgenden Befehl aus, um die Korrektheit Ihrer Arbeit zu überprüfen:

```bash
check50 -l inf-zone/exercises/2026/caesar
```

**Verwendung von `debug50`**

Möchten Sie `debug50` ausführen? Sie können dies wie folgt tun, nachdem Sie Ihren Code erfolgreich mit `make` kompiliert haben,

```bash
debug50 ./caesar KEY
```

wobei `KEY` der Schlüssel ist, den Sie als Kommandozeilenargument an Ihr Programm übergeben. Beachten Sie, dass die Ausführung von

```bash
debug50 ./caesar
```

(im Idealfall!) dazu führt, dass Ihr Programm mit `Usage: ./caesar key` endet, also nach dem fehlenden Schlüssel fragt.

### Style

Führen Sie den folgenden Befehl aus, um den Stil Ihres Codes mit `style50` zu analysieren:

```bash
style50 caesar.c
```

## Abgeben

Geben Sie im Ordner `caesar` ab:

```bash
inf upload caesar
```

Danach sehen Sie, welche Tests Ihr Programm besteht und ob die Übung für die Bonuspunkte zählt. Wie Sie `inf` installieren und sich anmelden, steht unter [Abgeben mit `inf`](https://dev.inf.zone/faq/uebung-solutions/#inf-installieren).

## Zum Weiterlesen: Cäsar und seine Geheimschrift

Für die Lösung der Aufgabe nicht nötig.

Angeblich hat Cäsar vertrauliche Nachrichten „verschlüsselt“ (d.h. umkehrbar unkenntlich gemacht), indem er jeden Buchstaben um eine bestimmte Anzahl von Stellen verschob. Zum Beispiel könnte er A als B geschrieben haben, B als C, C als D, ..., und am Ende beim Überschreiten des Alphabets, Z als A. Um jemandem "HELLO" zu sagen, könnte Cäsar also stattdessen "IFMMP" geschrieben haben. Der Empfänger dieser Nachricht müsste diese "entschlüsseln", indem er die Buchstaben um die gleiche Anzahl von Stellen in die entgegengesetzte Richtung verschiebt.

Die Sicherheit dieses "Kryptosystems" beruhte darauf, dass nur Cäsar und die Empfänger das Geheimnis kannten, nämlich die Anzahl der Stellen, um die Cäsar seine Buchstaben verschoben hatte (z.B. `1`). Nach heutigen Maßstäben ist diese Verschlüsselung nicht besonders sicher. Aber wenn man das womöglich als Erster überhaupt gemacht hat, ist es natürlich ziemlich sicher!
