

Task

Implement a Caesar cipher, both encoding and decoding. 

The key is an integer from 1 to 25.

This cipher rotates (either towards left or right) the letters of the alphabet (A to Z).

The encoding replaces each letter with the 1st to 25th next letter in the alphabet (wrapping Z to A).

So key 2 encrypts "HI" to "JK", but key 20 encrypts "HI" to "BC".

This simple "mono-alphabetic substitution cipher" provides almost no security, because an attacker who has the encoded message can either use frequency analysis to guess the key, or just try all 25 keys.

Caesar cipher is identical to Vigenère cipher with a key of length 1. 

Also, Rot-13 is identical to Caesar cipher with key 13.



Related tasks

 Rot-13
 Substitution Cipher
 Vigenère Cipher/Cryptanalysis


MODULE Caesar EXPORTS Main;
 
IMPORT IO, IntSeq, Text;
 
EXCEPTION BadCharacter;
(* Used whenever message contains a non-alphabetic character. *)
 
PROCEDURE Encode(READONLY message: TEXT; numbers: IntSeq.T) RAISES { BadCharacter } =
(*
Converts upper or lower case letter to 0..25.
Raises a "BadCharacter" exception for non-alphabetic characters.
*)
VAR
  c: CHAR;
  v: INTEGER;
BEGIN
  FOR i := 0 TO Text.Length(message) - 1 DO
    c := Text.GetChar(message, i);
    CASE c OF
    | 'A'..'Z' => v := ORD(c) - ORD('A');
    | 'a'..'z' => v := ORD(c) - ORD('a');
    ELSE
      RAISE BadCharacter;
    END;
    numbers.addhi(v);
  END;
END Encode;
 
PROCEDURE Decode(READONLY numbers: IntSeq.T; VAR message: TEXT) =
(* converts numbers in 0..26 to lower case characters *)
BEGIN
  FOR i := 0 TO numbers.size() - 1 DO
    message := message & Text.FromChar(VAL(numbers.get(i) + ORD('a'), CHAR));
  END;
END Decode;
 
PROCEDURE Crypt(numbers: IntSeq.T; key: INTEGER) =
(*
In the Caesar cipher, encryption and decryption are really the same;
one adds the key, the other subtracts it. We can view this as adding a positive
or nevative integer; the common task is adding an integer. We call this "Crypt".
*)
BEGIN
  FOR i := 0 TO numbers.size() - 1 DO
    numbers.put(i, (numbers.get(i) + key) MOD 26);
  END;
END Crypt;
 
PROCEDURE Encrypt(numbers: IntSeq.T; key := 4) =
(*
Encrypts a message of numbers using the designated key.
The result is also stored in "numbers".
*)
BEGIN
  Crypt(numbers, key);
END Encrypt;
 
PROCEDURE Decrypt(numbers: IntSeq.T; key := 4) =
(*
Decrypts a message of numbers using the designated key.
The result is also stored in "numbers".
*)
BEGIN
  Crypt(numbers, -key);
END Decrypt;
 
VAR
 
  message := "";
  buffer := NEW(IntSeq.T).init(22); (* sequence of 22 int's *)
 
BEGIN
  TRY
    Encode("WhenCaesarSetOffToGaul", buffer);
  EXCEPT BadCharacter =>
    (* 
      This should never occur.
      Try adding spaces to the above to see what happens.
    *)
    IO.Put("Encountered a bad character in the input; completing partial task\n");
  END;
  Encrypt(buffer);
  Decrypt(buffer);
  Decode(buffer, message);
  IO.Put(message); IO.PutChar('\n');
END Caesar.