Caesar cipher: Difference between revisions
(→{{header|Icon}} and {{header|Unicon}}: fix offset k, handle negative too) |
|||
Line 229: | Line 229: | ||
procedure caesar(text,k,mode) #: mono-alphabetic shift cipher |
procedure caesar(text,k,mode) #: mono-alphabetic shift cipher |
||
/k := 3 |
/k := 3 |
||
k := (((k % *&lcase) + *&lcase) % *&lcase) + 1 |
|||
case mode of { |
case mode of { |
||
&null|"e"|"encrypt": return map(text,&lcase,(&lcase||&lcase)[k+:*&lcase]) |
&null|"e"|"encrypt": return map(text,&lcase,(&lcase||&lcase)[k+:*&lcase]) |
||
Line 236: | Line 237: | ||
Output:<pre>Plain text = "the quick brown fox jumped over the lazy dog" |
Output:<pre>Plain text = "the quick brown fox jumped over the lazy dog" |
||
Encphered text = " |
Encphered text = "wkh txlfn eurzq ira mxpshg ryhu wkh odcb grj" |
||
Decphered text = "the quick brown fox jumped over the lazy dog"</pre> |
Decphered text = "the quick brown fox jumped over the lazy dog"</pre> |
||
Revision as of 08:52, 20 June 2011
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
Note: This specimen retains the original Ada coding style.
<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#;
- 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);
- "HAL"
CaesarCipher("Vgg cphvi wzdibn vmz wjmi amzz viy zlpvg di ydbidot viy mdbcon.", 5);
- "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 k := (((k % *&lcase) + *&lcase) % *&lcase) + 1 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 = "wkh txlfn eurzq ira mxpshg ryhu wkh odcb grj" 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
<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.