Caesar cipher

From Rosetta Code
Revision as of 22:11, 19 June 2011 by rosettacode>Kernigh (→‎{{header|Ruby}}: Array#rotate, not Range#rotate.)
Caesar cipher is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

Implement a Caesar cipher, both encryption and decryption. The key is an integer from 1 to 25. This cipher rotates the letters of the alphabet (A to Z). The encryption 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 "monoalphabetic substitution cipher" provides almost no security, because an attacker who has the encrypted 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 key of length 1. Also, Rot-13 is identical to Caesar cipher with key 13.

Ada

<lang Ada>with Ada.Text_IO;

procedure Caesar is

  type M26 is mod 26;
  function To_M26(C: Character; Offset: Character) return M26 is
  begin
     return M26(Character'Pos(C)-Character'Pos(Offset));
  end To_M26;
  function To_Character(Value:   in  M26; Offset: Character)
                       return Character is
  begin
     return Character'Val(Integer(Value)+Character'Pos(Offset));
  end To_Character;
  function Encrypt (Plain: String; Key: M26) return String is
     Ciph: String(Plain'Range);
  begin
     for I in Plain'Range loop
        case Plain(I) is
           when 'A' .. 'Z' =>
              Ciph(I) := To_Character(To_M26(Plain(I), 'A')+Key, 'A');
           when 'a' .. 'z' =>
              Ciph(I) := To_Character(To_M26(Plain(I), 'a')+Key, 'a');
           when others =>
              Ciph(I) := Plain(I);
        end case;
     end loop;
     return Ciph;
  end Encrypt;
  Text:  String := Ada.Text_IO.Get_Line;
  Key: M26 := 3; -- Default key from "Commentarii de Bello Gallico"

begin -- Caesar main program

  Ada.Text_IO.Put_Line("Plaintext ------------>" & Text);
  Text := Encrypt(Text, Key);
  Ada.Text_IO.Put_Line("Ciphertext ----------->" & Text);
  Ada.Text_IO.Put_Line("Decrypted Ciphertext ->" & Encrypt(Text, -Key));

end Caesar;</lang> Output

> ./caesar 
The five boxing wizards jump quickly
Plaintext ------------>The five boxing wizards jump quickly
Ciphertext ----------->Wkh ilyh eralqj zlcdugv mxps txlfnob
Decrypted Ciphertext ->The five boxing wizards jump quickly

ALGOL 68

Translation of: Ada

Note: This specimen retains the original Ada coding style.

Works with: ALGOL 68 version Revision 1 - no extensions to language used.
Works with: ALGOL 68G version Any - tested with release 1.18.0-9h.tiny.

<lang algol68>#!/usr/local/bin/a68g --script #

program caesar: BEGIN

  MODE MODXXVI = SHORT SHORT INT; # MOD26 #
  PROC to m26 = (CHAR c, offset)MODXXVI:
  BEGIN
     ABS c - ABS offset
  END #to m26#;
  PROC to char = (MODXXVI value, CHAR offset)CHAR:
  BEGIN
     REPR ( ABS offset + value MOD 26 )
  END #to char#;
  PROC encrypt = (STRING plain, MODXXVI key)STRING:
  BEGIN
     [UPB plain]CHAR ciph;
     FOR i TO UPB plain DO
        CHAR c = plain[i];
        ciph[i]:=
          IF "A" <= c AND c <= "Z" THEN
              to char(to m26(c, "A")+key, "A")
          ELIF "a" <= c AND c <= "z" THEN
              to char(to m26(c, "a")+key, "a")
          ELSE
              c
          FI
     OD;
     ciph
  END #encrypt#;
  1. caesar main program #
  STRING text := "The five boxing wizards jump quickly" # OR read string #;
  MODXXVI key := 3; # Default key from "Bello Gallico" #
  printf(($gl$, "Plaintext ------------>" + text));
  text := encrypt(text, key);
  printf(($gl$, "Ciphertext ----------->" + text));
  printf(($gl$, "Decrypted Ciphertext ->" + encrypt(text, -key)))

END #caesar#</lang> Output:

Plaintext ------------>The five boxing wizards jump quickly
Ciphertext ----------->Wkh ilyh eralqj zlcdugv mxps txlfnob
Decrypted Ciphertext ->The five boxing wizards jump quickly

D

<lang d>import std.stdio, std.traits, std.utf, std.ctype;

S rot(S)(S s, in int key) if (isSomeString!S) {

   Unqual!(typeof(s[0]))[] r;
       
   foreach (dchar c; s) {
       const ubyte cap = c & 32;
       c &= ~cap;
       c = (isupper(c) ? ((c - 'A' + key) % 26 + 'A') : c) | cap;
       std.utf.encode(r, c);        
   }
   return cast(S) r;

}

void main() {

   enum key = 3;
   auto txt = "The five boxing wizards jump quickly";
   writeln("Original:  ", txt);
   writeln("Encrypted: ", txt.rot(key));
   writeln("Decrypted: ", txt.rot(key).rot(26-key));

}</lang>

Original:  The five boxing wizards jump quickly
Encrypted: Wkh ilyh eralqj zlcdugv mxps txlfnob
Decrypted: The five boxing wizards jump quickly

Fortran

<lang fortran>program Caesar_Cipher

 implicit none
 integer, parameter :: key = 3     
 character(43) :: message = "The five boxing wizards jump quickly"
 write(*, "(2a)") "Original message  = ", message
 call encrypt(message)
 write(*, "(2a)") "Encrypted message = ", message
 call decrypt(message)
 write(*, "(2a)") "Decrypted message = ", message
 

contains

subroutine encrypt(text)

 character(*), intent(inout) :: text
 integer :: i
 
 do i = 1, len(text)
   select case(text(i:i))
     case ('A':'Z')
       text(i:i) = achar(modulo(iachar(text(i:i)) - 65 + key, 26) + 65)
     case ('a':'z')
       text(i:i) = achar(modulo(iachar(text(i:i)) - 97 + key, 26) + 97)
   end select
 end do

end subroutine

subroutine decrypt(text)

 character(*), intent(inout) :: text
 integer :: i
 
 do i = 1, len(text)
   select case(text(i:i))
     case ('A':'Z')
       text(i:i) = achar(modulo(iachar(text(i:i)) - 65 - key, 26) + 65)
     case ('a':'z')
       text(i:i) = achar(modulo(iachar(text(i:i)) - 97 - key, 26) + 97)
   end select
 end do

end subroutine

end program Caesar_Cipher</lang> Output

Original message  = The five boxing wizards jump quickly
Encrypted message = Wkh ilyh eralgj zlcdugv mxps txlfnob
Decrypted message = The five boxing wizards jump quickly

GAP

<lang gap>CaesarCipher := function(s, n) local r, c, i, lower, upper; lower := "abcdefghijklmnopqrstuvwxyz"; upper := "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; r := ""; for c in s do i := Position(lower, c); if i <> fail then Add(r, lower[RemInt(i + n - 1, 26) + 1]); else i := Position(upper, c); if i <> fail then Add(r, upper[RemInt(i + n - 1, 26) + 1]); else Add(r, c); fi; fi; od; return r; end;

CaesarCipher("IBM", 25);

  1. "HAL"

CaesarCipher("Vgg cphvi wzdibn vmz wjmi amzz viy zlpvg di ydbidot viy mdbcon.", 5);

  1. "All human beings are born free and equal in dignity and rights."</lang>

Icon and Unicon

Strictly speaking a Ceasar Cipher is a shift of 3 (the default in this case). <lang Icon>procedure main() ctext := caesar(ptext := map("The quick brown fox jumped over the lazy dog")) dtext := caesar(ctext,,"decrypt") write("Plain text = ",image(ptext)) write("Encphered text = ",image(ctext)) write("Decphered text = ",image(dtext)) end

procedure caesar(text,k,mode) #: mono-alphabetic shift cipher /k := 3 case mode of {

 &null|"e"|"encrypt": return map(text,&lcase,(&lcase||&lcase)[k+:*&lcase])
 "d"|"decrypt"      : return map(text,(&lcase||&lcase)[k+:*&lcase],&lcase)
 }

end</lang>

Output:

Plain text  = "the quick brown fox jumped over the lazy dog"
Encphered text = "vjg swkem dtqyp hqz lworgf qxgt vjg ncba fqi"
Decphered text = "the quick brown fox jumped over the lazy dog"

J

If we assume that the task also requires us to leave non-alphabetic characters alone:

<lang j>cndx=: [: , 65 97 +/ 26 | (i.26)&+ caesar=: (cndx 0)}&a.@u:@cndx@[ {~ a.i.]</lang>

Example use:<lang j> 2 caesar 'This simple "monoalphabetic substitution cipher" provides almost no security, ...' Vjku ukorng "oqpqcnrjcdgvke uwduvkvwvkqp ekrjgt" rtqxkfgu cnoquv pq ugewtkva, ...</lang>

Perl 6

<lang perl6>my @alpha = 'A' .. 'Z'; sub encrypt ( $key where 1..25, $plaintext ) {

   $plaintext.trans( @alpha Z=> @alpha.rotate($key) );

} sub decrypt ( $key where 1..25, $cyphertext ) {

   $cyphertext.trans( @alpha.rotate($key) Z=> @alpha );

}

my $original = 'THE FIVE BOXING WIZARDS JUMP QUICKLY'; my $en = encrypt( 13, $original ); my $de = decrypt( 13, $en );

.say for $original, $en, $de;

say 'OK' if $original eq all( map { .&decrypt(.&encrypt($original)) }, 1..25 );</lang>

Output:

THE FIVE BOXING WIZARDS JUMP QUICKLY
GUR SVIR OBKVAT JVMNEQF WHZC DHVPXYL
THE FIVE BOXING WIZARDS JUMP QUICKLY
OK

PicoLisp

<lang PicoLisp>(setq *Letters (apply circ (mapcar char (range 65 90))))

(de caesar (Str Key)

  (pack
     (mapcar '((C) (cadr (nth (member C *Letters) Key)))
        (chop (uppc Str)) ) ) )</lang>

Test:

: (caesar "IBM" 25)
-> "HAL"
: (caesar @ 1)
-> "IBM"

: (caesar "The quick brown fox jumped over the lazy dog's back" 7)
-> "AOLXBPJRIYVDUMVEQBTWLKVCLYAOLSHGFKVNZIHJR"
: (caesar @ (- 26 7))
-> "THEQUICKBROWNFOXJUMPEDOVERTHELAZYDOGSBACK"

PureBasic

The case is maintained for alphabetic characters (uppercase/lowercase input = uppercase/lowercase output) while non-alphabetic characters, if present are included and left unchanged in the result. <lang PureBasic>Procedure.s CC_encrypt(plainText.s, key, reverse = 0)

 ;if reverse <> 0 then reverse the encryption (decrypt)
 If reverse: reverse = 26: key = 26 - key: EndIf
 
 Static alphabet$ = "ABCEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
 
 Protected result.s, i, length = Len(plainText), letter.s, legal
 If key < 1 Or key > 25: ProcedureReturn: EndIf  ;keep key in range
  
 For i = 1 To length
   letter = Mid(plainText, i, 1)
   legal = FindString(alphabet$, letter, 1 + reverse)
   If legal
     result + Mid(alphabet$, legal + key, 1)
   Else
     result + letter
   EndIf 
 Next 
 ProcedureReturn result

EndProcedure

Procedure.s CC_decrypt(cypherText.s, key)

 ProcedureReturn CC_encrypt(cypherText, key, 1)

EndProcedure

If OpenConsole()

 Define key, plainText.s, encryptedText.s, decryptedText.s
 
 key = Random(24) + 1 ;get a random key in the range 1 -> 25
 
 plainText = "The quick brown fox jumped over the lazy dogs.": PrintN(RSet("Plain text = ", 17) + #DQUOTE$ + plainText + #DQUOTE$)
 encryptedText = CC_encrypt(plainText, key): PrintN(RSet("Encrypted text = ", 17) + #DQUOTE$ + encryptedText + #DQUOTE$)
 decryptedText = CC_decrypt(encryptedText, key): PrintN(RSet("Decrypted text = ", 17) + #DQUOTE$ + decryptedText + #DQUOTE$)
 
 Print(#CRLF$ + #CRLF$ + "Press ENTER to exit"): Input()
 CloseConsole()

EndIf</lang> Sample output:

    Plain text = "The quick brown fox jumped over the lazy dogs."
Encrypted text = "Znk waoiq hxuct lud pasvkj ubkx znk rgfe jumy."
Decrypted text = "the quick brown fox jumped over the lazy dogs."

Alternate solution

Here is an alternate and more advanced form of the encrypt procedure. It improves on the simple version in terms of speed, in case Caesar is using the cipher on some very long documents. It is meant to replace the encrypt procedure in the previous code and produces identical results. <lang PureBasic>Procedure.s CC_encrypt(text.s, key, reverse = 0)

 ;if reverse <> 0 then reverse the encryption (decrypt)
 Protected i, *letter.Character, *resultLetter.Character, result.s = Space(Len(text))
 
 If reverse: key = 26 - key: EndIf
 If key < 1 Or key > 25: ProcedureReturn: EndIf  ;exit if key out of range
  
 *letter = @text: *resultLetter = @result
 While *letter\c
   Select *letter\c
     Case 'A' To 'Z'
       *resultLetter\c = ((*letter\c - 65 + key) % 26) + 65
     Case 'a' To 'z'
       *resultLetter\c = ((*letter\c - 97 + key) % 26) + 97
     Default
       *resultLetter\c = *letter\c
   EndSelect
   *letter + SizeOf(Character): *resultLetter + SizeOf(Character)
 Wend
 ProcedureReturn result

EndProcedure</lang>

Ruby

Translation of: Perl 6

<lang ruby>@atoz = Hash.new do |hash, key|

 str = ('A'..'Z').to_a.rotate(key).join("")
 hash[key] = (str << str.downcase)

end

def encrypt(key, plaintext)

 (1..25) === key or raise ArgumentError, "key not in 1..25"
 plaintext.tr(@atoz[0], @atoz[key])

end

def decrypt(key, ciphertext)

 (1..25) === key or raise ArgumentError, "key not in 1..25"
 ciphertext.tr(@atoz[key], @atoz[0])

end

original = "THEYBROKEOURCIPHEREVERYONECANREADTHIS" en = encrypt(3, original) de = decrypt(3, en)

[original, en, de].each {|e| puts e}

puts 'OK' if

 (1..25).all? {|k| original == decrypt(k, encrypt(k, original))}</lang>

The Ruby translation saves the rotations in an @atoz hash. (The Perl 6 code called rotate() during each encrypt or decrypt.) The code block on Hash.new makes the rotation and puts it in the hash.

  • @atoz[0] becomes "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
  • @atoz[3] becomes "DEFGHIJKLMNOPQRSTUVWXYZABCdefghijklmnopqrstuvwxyzabc",
  • and so on.

When the key is 3, encryption uses String#tr(@atoz[0], @atoz[3]) to substitute characters. Decryption uses String#tr(@atoz[3], @atoz[0]).

To make a rotation, Ruby is worse than Perl 6. Ruby needs two extra conversions: the code ('A'..'Z').to_a.rotate(key).join("") uses to_a to convert a Range to an Array, to access the Array#rotate method. Then join("") converts the rotated Array to a String, because String#tr needs a String, not an Array.

Tcl

<lang tcl>package require Tcl 8.6; # Or TclOO package for 8.5

oo::class create Caesar {

   variable encryptMap decryptMap
   constructor shift {

for {set i 0} {$i < 26} {incr i} { # Play fast and loose with string/list duality for shorter code append encryptMap [format "%c %c %c %c " \ [expr {$i+65}] [expr {($i+$shift)%26+65}] \ [expr {$i+97}] [expr {($i+$shift)%26+97}]] append decryptMap [format "%c %c %c %c " \ [expr {$i+65}] [expr {($i-$shift)%26+65}] \ [expr {$i+97}] [expr {($i-$shift)%26+97}]] }

   }
   method encrypt text {

string map $encryptMap $text

   }
   method decrypt text {

string map $decryptMap $text

   }

}</lang> Demonstrating: <lang tcl>set caesar [Caesar new 3] set txt "The five boxing wizards jump quickly." set enc [$caesar encrypt $txt] set dec [$caesar decrypt $enc] puts "Original message = $txt" puts "Encrypted message = $enc" puts "Decrypted message = $dec"</lang> Output:

Original message  = The five boxing wizards jump quickly.
Encrypted message = Wkh ilyh eralqj zlcdugv mxps txlfnob.
Decrypted message = The five boxing wizards jump quickly.