Vigenère cipher/Cryptanalysis: Difference between revisions
Thundergnat (talk | contribs) (Rename Perl 6 -> Raku, alphabetize, minor clean-up) |
m (→{{header|Wren}}: Minor tidy) |
||
(13 intermediate revisions by 10 users not shown) | |||
Line 1: | Line 1: | ||
{{task|Encryption}} |
{{task|Encryption}}[[Category:Encryption]] |
||
Given some text you suspect has been encrypted with a Vigenère cipher, extract the key and plaintext. There are several methods for doing this. See [[wp:Vigenère_cipher#Cryptanalysis|the Wikipedia entry]] for more information. Use the following encrypted text: |
|||
[[Category:String manipulation]] |
|||
<pre> |
|||
{{omit from|GUISS|would need to install an application that could do this}} |
|||
MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH |
|||
{{omit from|Openscad}} |
|||
VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD |
|||
ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS |
|||
FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG |
|||
ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ |
|||
ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS |
|||
JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT |
|||
LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST |
|||
MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH |
|||
QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV |
|||
RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW |
|||
TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO |
|||
SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR |
|||
ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX |
|||
BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB |
|||
BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA |
|||
FWAML ZZRXJ EKAHV FASMU LVVUT TGK |
|||
</pre> |
|||
Letter frequencies for English can be found [[wp:Letter_frequency|here]]. |
|||
;Task: |
|||
Implement a [[wp:Vigen%C3%A8re_cipher|Vigenère cypher]], both encryption and decryption. |
|||
Specifics for this task: |
|||
The program should handle keys and text of unequal length, |
|||
* Take only the ciphertext as input. You can assume it's all capitalized and has no punctuation, but it might have whitespace. |
|||
and should capitalize everything and discard non-alphabetic characters. <br> |
|||
* Assume the plaintext is written in English. |
|||
(If your program handles non-alphabetic characters in another way, |
|||
* Find and output the key. |
|||
make a note of it.) |
|||
* Use that key to decrypt and output the original plaintext. Maintaining the whitespace from the ciphertext is optional. |
|||
* The algorithm doesn't have to be perfect (which may not be possible) but it should work when given enough ciphertext. The example above is fairly long, and should be plenty for any algorithm. |
|||
=={{header|11l}}== |
|||
{{trans|Python}} |
|||
<syntaxhighlight lang="11l">-V ascii_uppercase = Array(‘A’..‘Z’) |
|||
;Related tasks: |
|||
* [[Caesar cipher]] |
|||
* [[Rot-13]] |
|||
* [[Substitution Cipher]] |
|||
<br><br> |
|||
F vigenere_decrypt(target_freqs, input) |
|||
=={{header|Ada}}== |
|||
V nchars = :ascii_uppercase.len |
|||
V ordA = ‘A’.code |
|||
V sorted_targets = sorted(target_freqs) |
|||
F frequency(input) |
|||
<lang Ada>WITH Ada.Text_IO, Ada.Characters.Handling; |
|||
V result = :ascii_uppercase.map(c -> (c, 0.0)) |
|||
USE Ada.Text_IO, Ada.Characters.Handling; |
|||
L(c) input |
|||
result[c - @ordA][1]++ |
|||
R result |
|||
F correlation(input) |
|||
PROCEDURE Main IS |
|||
V result = 0.0 |
|||
V freq = sorted(@frequency(input), key' a -> a[1]) |
|||
TYPE Ring IS MOD (Alpha'Range_length); |
|||
TYPE Seq IS ARRAY (Integer RANGE <>) OF Ring; |
|||
FUNCTION "+" (S, Key : Seq) RETURN Seq IS |
|||
R : Seq (S'Range); |
|||
BEGIN |
|||
FOR I IN R'Range LOOP |
|||
R (I) := S (I) + Key (Key'First + (I - R'First) MOD Key'Length); |
|||
END LOOP; |
|||
RETURN R; |
|||
END "+"; |
|||
FUNCTION "-" (S : Seq) RETURN Seq IS |
|||
R : Seq (S'Range); |
|||
BEGIN |
|||
FOR I IN R'Range LOOP |
|||
R (I) := - S (I); |
|||
END LOOP; |
|||
RETURN R; |
|||
END "-"; |
|||
FUNCTION To_Seq (S : String) RETURN Seq IS |
|||
R : Seq (S'Range); |
|||
I : Integer := R'First; |
|||
BEGIN |
|||
FOR C OF To_Upper (S) LOOP |
|||
IF C IN Alpha THEN |
|||
R (I) := Ring'Mod (Alpha'Pos (C) - Alpha'Pos (Alpha'First)); |
|||
I := I + 1; |
|||
END IF; |
|||
END LOOP; |
|||
RETURN R (R'First .. I - 1); |
|||
END To_Seq; |
|||
FUNCTION To_String (S : Seq ) RETURN String IS |
|||
R : String (S'Range); |
|||
BEGIN |
|||
FOR I IN R'Range LOOP |
|||
R (I) := Alpha'Val ( Integer (S (I)) + Alpha'Pos (Alpha'First)); |
|||
END LOOP; |
|||
RETURN R; |
|||
END To_String; |
|||
Input : Seq := To_Seq (Get_Line); |
|||
Key : Seq := To_Seq (Get_Line); |
|||
Crypt : Seq := Input + Key; |
|||
BEGIN |
|||
Put_Line ("Encrypted: " & To_String (Crypt)); |
|||
Put_Line ("Decrypted: " & To_String (Crypt + (-Key))); |
|||
END Main; |
|||
L(f) freq |
|||
</lang> |
|||
result += f[1] * @sorted_targets[L.index] |
|||
R result |
|||
V cleaned = input.uppercase().filter(c -> c.is_uppercase()).map(c -> c.code) |
|||
{{out}} |
|||
V best_len = 0 |
|||
<pre>Beware the Jabberwock, my son! The jaws that bite, the claws that catch! |
|||
V best_corr = -100.0 |
|||
VIGENERECIPHER |
|||
Encrypted: WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY |
|||
Decrypted: BEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCH</pre> |
|||
L(i) 2 .< cleaned.len I/ 20 |
|||
=={{header|ALGOL 68}}== |
|||
V pieces = [[Int]()] * i |
|||
{{trans|C++}} Note: This specimen retains the original [[Vigenère_Cipher#C++|C++]] coding style. |
|||
L(c) cleaned |
|||
{{works with|ALGOL 68|Revision 1 - no extensions to language used.}} |
|||
pieces[L.index % i].append(c) |
|||
{{works with|ALGOL 68G|Any - tested with release [http://sourceforge.net/projects/algol68/files/algol68g/algol68g-1.18.0/algol68g-1.18.0-9h.tiny.el5.centos.fc11.i386.rpm/download 1.18.0-9h.tiny].}} |
|||
V corr = -0.5 * i + sum(pieces.map(p -> @correlation(p))) |
|||
{{works with|ELLA ALGOL 68|Any (with appropriate job cards) - tested with release [http://sourceforge.net/projects/algol68/files/algol68toc/algol68toc-1.8.8d/algol68toc-1.8-8d.fc9.i386.rpm/download 1.8-8d].}} |
|||
<lang algol68>STRING key := ""; |
|||
I corr > best_corr |
|||
PROC vigenere cipher = (REF STRING key)VOID: |
|||
best_len = i |
|||
( |
|||
best_corr = corr |
|||
IF key[i] >= "A" AND key[i] <= "Z" THEN |
|||
key +:= key[i] FI; |
|||
IF key[i] >= "a" AND key[i] <= "z" THEN |
|||
key +:= REPR(ABS key[i] + ABS"A" - ABS"a") FI |
|||
OD |
|||
); |
|||
I best_len == 0 |
|||
PROC encrypt = (STRING text)STRING: |
|||
R (‘Text is too short to analyze’, ‘’) |
|||
( |
|||
STRING out := ""; |
|||
V pieces = [[Int]()] * best_len |
|||
INT j := LWB text; |
|||
L(c) cleaned |
|||
FOR i FROM LWB text TO UPB text DO |
|||
pieces[L.index % best_len].append(c) |
|||
CHAR c := text[i]; |
|||
V freqs = pieces.map(p -> @frequency(p)) |
|||
IF c >= "a" AND c <= "z" THEN |
|||
c := REPR(ABS c + (ABS"A" - ABS"a")) FI; |
|||
IF c >= "A" AND c <= "Z" THEN |
|||
out +:= REPR((ABS c + ABS key[j] - 2*ABS"A") MOD 26 + ABS"A"); |
|||
j := j MOD UPB key + 1 |
|||
FI |
|||
OD; |
|||
V key = ‘’ |
|||
out |
|||
L(fr_) freqs |
|||
); |
|||
V fr = sorted(fr_, key' a -> a[1], reverse' 1B) |
|||
V m = 0 |
|||
V max_corr = 0.0 |
|||
L(j) 0 .< nchars |
|||
V corr = 0.0 |
|||
V c = ordA + j |
|||
L(frc) fr |
|||
V d = (frc[0].code - c + nchars) % nchars |
|||
corr += frc[1] * target_freqs[d] |
|||
I corr > max_corr |
|||
PROC decrypt = (STRING text)STRING: |
|||
m = j |
|||
( |
|||
max_corr = corr |
|||
STRING out; |
|||
key ‘’= Char(code' m + ordA) |
|||
INT j := LWB text; |
|||
FOR i FROM LWB text TO UPB text DO |
|||
CHAR c := text[i]; |
|||
V r = (enumerate(cleaned).map((i, c) -> Char(code' (c - @key[i % @best_len].code + @nchars) % @nchars + @ordA))) |
|||
IF c >= "a" AND c <= "z" THEN |
|||
R (key, r.join(‘’)) |
|||
c := REPR(ABS c + (ABS"A" - ABS"a")) FI; |
|||
IF c >= "A" AND c <= "Z" THEN |
|||
out +:= REPR((ABS c - ABS key[j] + 26) MOD 26 + ABS"A"); |
|||
j := j MOD UPB key + 1 |
|||
FI |
|||
OD; |
|||
V encoded = ‘ |
|||
out |
|||
MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH |
|||
); |
|||
VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD |
|||
ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS |
|||
FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG |
|||
ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ |
|||
ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS |
|||
JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT |
|||
LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST |
|||
MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH |
|||
QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV |
|||
RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW |
|||
TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO |
|||
SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR |
|||
ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX |
|||
BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB |
|||
BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA |
|||
FWAML ZZRXJ EKAHV FASMU LVVUT TGK’ |
|||
V english_frequences = [ |
|||
main: |
|||
0.08167, 0.01492, 0.02782, 0.04253, 0.12702, 0.02228, 0.02015, |
|||
( |
|||
0.06094, 0.06966, 0.00153, 0.00772, 0.04025, 0.02406, 0.06749, |
|||
vigenere cipher(key:="VIGENERECIPHER"); |
|||
0.07507, 0.01929, 0.00095, 0.05987, 0.06327, 0.09056, 0.02758, |
|||
0.00978, 0.02360, 0.00150, 0.01974, 0.00074] |
|||
V (key, decoded) = vigenere_decrypt(english_frequences, encoded) |
|||
STRING original := "Beware the Jabberwock, my son! The jaws that bite, the claws that catch!"; |
|||
print(‘Key: ’key) |
|||
STRING encrypted := encrypt(original); |
|||
print("\nText: "decoded)</syntaxhighlight> |
|||
STRING decrypted := decrypt(encrypted); |
|||
print((original, new line)); |
|||
print(("Encrypted: ", encrypted, new line)); |
|||
print(("Decrypted: ", decrypted, new line)) |
|||
)</lang> |
|||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
Key: THECHESHIRECAT |
|||
Beware the Jabberwock, my son! The jaws that bite, the claws that catch! |
|||
Encrypted: WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY |
|||
Text: THISWASTHEPOEMTHATALICEREADJABBERWOCKYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIMBLEINTHEWABEALLMIMSYWERETHEBOROGOVE... |
|||
Decrypted: BEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCH |
|||
</pre> |
</pre> |
||
=={{header| |
=={{header|Ada}}== |
||
The program is not fully auto, but makes a small number of suggestions for the right key and plaintext. |
|||
Lines <code>340,350,430,440</code> could probably been put into some DEF FN, but it would probably have made it harder to read. The maximum length for a string in AppleSoft BASIC is 255 characters. |
|||
<syntaxhighlight lang="ada">with Ada.Text_IO; |
|||
I have not used the DEF FN MOD(A) function in line <code>450</code> on purpose, as I still would have had to correct for a possible negative value. |
|||
<lang Applesoft BASIC> |
|||
100 : |
|||
110 REM VIGENERE CIPHER |
|||
120 : |
|||
200 REM SET-UP |
|||
210 K$ = "LEMON": PRINT "KEY: "; K$ |
|||
220 PT$ = "ATTACK AT DAWN": PRINT "PLAIN TEXT: ";PT$ |
|||
230 DEF FN MOD(A) = A - INT (A / 26) * 26 |
|||
300 REM ENCODING |
|||
310 K = 1 |
|||
320 FOR I = 1 TO LEN (PT$) |
|||
330 IF ASC ( MID$ (PT$,I,1)) < 65 |
|||
OR ASC ( MID$ (PT$,I,1)) > 90 THEN NEXT I |
|||
340 TV = ASC ( MID$ (PT$,I,1)) - 65 |
|||
350 KV = ASC ( MID$ (K$,K,1)) - 65 |
|||
360 CT$ = CT$ + CHR$ ( FN MOD(TV + KV) + 65) |
|||
370 K = K + 1: IF K > LEN (K$) THEN K = 1 |
|||
380 NEXT I |
|||
390 PRINT "CIPHER TEXT: ";CT$ |
|||
400 REM DECODING |
|||
410 K = 1 |
|||
420 FOR I = 1 TO LEN (CT$) |
|||
430 TV = ASC ( MID$ (CT$,I,1)) - 65 |
|||
440 KV = ASC ( MID$ (K$,K,1)) - 65 |
|||
450 T = TV - KV: IF T < 0 THEN T = T + 26 |
|||
460 DT$ = DT$ + CHR$ (T + 65) |
|||
470 K = K + 1: IF K > LEN (K$) THEN K = 1 |
|||
480 NEXT I |
|||
490 PRINT "DECRYPTED TEXT: ";DT$ </lang> |
|||
{{out}} |
|||
KEY: LEMON |
|||
PLAIN TEXT: ATTACK AT DAWN |
|||
CIPHER TEXT: LXFOPVEFRNHR |
|||
DECRYPTED TEXT: ATTACKATDAWN |
|||
procedure Vignere_Cryptanalysis is |
|||
=={{header|AutoHotkey}}== |
|||
<lang AutoHotkey>Key = VIGENERECIPHER |
|||
Text= Beware the Jabberwock, my son! The jaws that bite, the claws that catch! |
|||
subtype Letter is Character range 'A' .. 'Z'; |
|||
out := "Input =" text "`nkey =" key "`nCiphertext =" (c := VigenereCipher(Text, Key)) "`nDecrypted =" VigenereDecipher(c, key) |
|||
MsgBox % clipboard := out |
|||
function "+"(X, Y: Letter) return Letter is |
|||
VigenereCipher(Text, Key){ |
|||
begin |
|||
StringUpper, Text, Text |
|||
return Character'Val( ( (Character'Pos(X)-Character'Pos('A')) |
|||
Text := RegExReplace(Text, "[^A-Z]") |
|||
+ (Character'Pos(Y)-Character'Pos('A')) ) mod 26 |
|||
Loop Parse, Text |
|||
+ Character'Pos('A')); |
|||
{ |
|||
end; |
|||
a := Asc(A_LoopField) - Asc("A") |
|||
b := Asc(SubStr(Key, 1+Mod(A_Index-1, StrLen(Key)), 1)) - Asc("A") |
|||
out .= Chr(Mod(a+b,26)+Asc("A")) |
|||
} |
|||
return out |
|||
} |
|||
function "-"(X, Y: Letter) return Letter is |
|||
VigenereDecipher(Text, key){ |
|||
begin |
|||
Loop Parse, key |
|||
return Character'Val( ( (Character'Pos(X)-Character'Pos('A')) |
|||
decoderKey .= Chr(26-(Asc(A_LoopField)-65)+65) |
|||
- (Character'Pos(Y)-Character'Pos('A')) ) mod 26 |
|||
return VigenereCipher(Text, decoderKey) |
|||
+ Character'Pos('A')); |
|||
}</lang> |
|||
end; |
|||
{{out}} |
|||
<pre>Input =Beware the Jabberwock, my son! The jaws that bite, the claws that catch! |
|||
key =VIGENERECIPHER |
|||
Ciphertext =WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY |
|||
Decrypted =BEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCH</pre> |
|||
type Frequency_Array is array (Letter) of Float; |
|||
=={{header|BBC BASIC}}== |
|||
<lang bbcbasic> key$ = "LEMON" |
|||
plaintext$ = "ATTACK AT DAWN" |
|||
ciphertext$ = FNencrypt(plaintext$, key$) |
|||
PRINT "Key = """ key$ """" |
|||
PRINT "Plaintext = """ plaintext$ """" |
|||
PRINT "Ciphertext = """ ciphertext$ """" |
|||
PRINT "Decrypted = """ FNdecrypt(ciphertext$, key$) """" |
|||
END |
|||
DEF FNencrypt(plain$, key$) |
|||
LOCAL i%, k%, n%, o$ |
|||
plain$ = FNupper(plain$) |
|||
key$ = FNupper(key$) |
|||
FOR i% = 1 TO LEN(plain$) |
|||
n% = ASCMID$(plain$, i%) |
|||
IF n% >= 65 IF n% <= 90 THEN |
|||
o$ += CHR$(65 + (n% + ASCMID$(key$, k%+1)) MOD 26) |
|||
k% = (k% + 1) MOD LEN(key$) |
|||
ENDIF |
|||
NEXT |
|||
= o$ |
|||
DEF FNdecrypt(cipher$, key$) |
|||
LOCAL i%, k%, n%, o$ |
|||
cipher$ = FNupper(cipher$) |
|||
key$ = FNupper(key$) |
|||
FOR i% = 1 TO LEN(cipher$) |
|||
n% = ASCMID$(cipher$, i%) |
|||
o$ += CHR$(65 + (n% + 26 - ASCMID$(key$, k%+1)) MOD 26) |
|||
k% = (k% + 1) MOD LEN(key$) |
|||
NEXT |
|||
= o$ |
|||
DEF FNupper(A$) |
|||
LOCAL A%,C% |
|||
FOR A% = 1 TO LEN(A$) |
|||
C% = ASCMID$(A$,A%) |
|||
IF C% >= 97 IF C% <= 122 MID$(A$,A%,1) = CHR$(C%-32) |
|||
NEXT |
|||
= A$</lang> |
|||
{{out}} |
|||
<pre> |
|||
Key = "LEMON" |
|||
Plaintext = "ATTACK AT DAWN" |
|||
Ciphertext = "LXFOPVEFRNHR" |
|||
Decrypted = "ATTACKATDAWN" |
|||
</pre> |
|||
English: Frequency_Array := |
|||
=={{header|Befunge}}== |
|||
( 0.08167, 0.01492, 0.02782, 0.04253, 0.12702, 0.02228, 0.02015, |
|||
The text to encrypt is read from stdin. The key is the string literal at the start of the program. |
|||
0.06094, 0.06966, 0.00153, 0.00772, 0.04025, 0.02406, 0.06749, |
|||
0.07507, 0.01929, 0.00095, 0.05987, 0.06327, 0.09056, 0.02758, |
|||
0.00978, 0.02360, 0.00150, 0.01974, 0.00074 ); |
|||
function Get_Frequency(S: String) return Frequency_Array is |
|||
<lang befunge>"VIGENERECIPHER">>>>1\:!v>"A"-\:00p0v |
|||
Result: Frequency_Array := (others => 0.0); |
|||
>>!#:0#-0#1g#,*#<+:v:-1$_^#!:\+1g00p< |
|||
Offset: Float := 1.0/Float(S'Length); |
|||
\"{"\v>9+2*%"A"+^>$>~>:48*\`#@_::"`"` |
|||
begin |
|||
*84*`<^4+"4"+g0\_^#!+`*55\`\0::-"A"-*</lang> |
|||
for I in S'Range loop |
|||
if S(I) in Letter then |
|||
Result(S(I)) := Result(S(I)) + Offset; |
|||
end if; |
|||
end loop; |
|||
return Result; |
|||
end Get_Frequency; |
|||
function Remove_Whitespace(S: String) return String is |
|||
{{out}} |
|||
begin |
|||
<pre>Beware the Jabberwock, my son! The jaws that bite, the claws that catch! |
|||
if S="" then |
|||
WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY</pre> |
|||
return ""; |
|||
elsif S(S'First) in Letter then |
|||
return S(S'First) & Remove_Whitespace(S(S'First+1 .. S'Last)); |
|||
else |
|||
return Remove_Whitespace(S(S'First+1 .. S'Last)); |
|||
end if; |
|||
end Remove_Whitespace; |
|||
function Distance(A, B: Frequency_Array; |
|||
The decrypter is essentially identical, except for a change of sign on the last line. |
|||
Offset: Character := 'A') return Float is |
|||
Result: Float := 0.0; |
|||
Diff: Float; |
|||
begin |
|||
for C in A'Range loop |
|||
Diff := A(C+Offset) - B(C); |
|||
Result := Result + (Diff * Diff); |
|||
end loop; |
|||
return Result; |
|||
end Distance; |
|||
function Find_Key(Cryptogram: String; Key_Length: Positive) return String is |
|||
<lang befunge>"VIGENERECIPHER">>>>1\:!v>"A"-\:00p0v |
|||
>>!#:0#-0#1g#,*#<+:v:-1$_^#!:\+1g00p< |
|||
\"{"\v>9+2*%"A"+^>$>~>:48*\`#@_::"`"` |
|||
*84*`<^4+"4"-g0\_^#!+`*55\`\0::-"A"-*</lang> |
|||
function Find_Caesar_Key(S: String) return Letter is |
|||
{{out}} |
|||
Frequency: Frequency_Array := Get_Frequency(S); |
|||
<pre>WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY |
|||
Candidate: Letter := 'A'; -- a fake candidate |
|||
BEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCH</pre> |
|||
Candidate_Dist : Float := Distance(Frequency, English, 'A'); |
|||
New_Dist: Float; |
|||
begin |
|||
=={{header|C}}== |
|||
This program skips non-alphabetical characters, preserves case, and when run with the <code>-d</code> command line flag, decrypts the message rather than encrypting. |
|||
<lang C>#include <stdio.h> |
|||
#include <stdlib.h> |
|||
#include <string.h> |
|||
#include <stdbool.h> |
|||
#include <ctype.h> |
|||
#include <getopt.h> |
|||
for L in Letter range 'B' .. 'Z' loop |
|||
#define NUMLETTERS 26 |
|||
New_Dist := Distance(Frequency, English, L); |
|||
#define BUFSIZE 4096 |
|||
if New_Dist <= Candidate_Dist then |
|||
Candidate_Dist := New_Dist; |
|||
Candidate := L; |
|||
end if; |
|||
end loop; |
|||
return Candidate; |
|||
end Find_Caesar_Key; |
|||
function Get_Slide(S: String; Step: Positive) return String is |
|||
char *get_input(void); |
|||
begin |
|||
if S'Length= 0 then |
|||
return ""; |
|||
else |
|||
return S(S'First) & Get_Slide(S(S'First+Step .. S'Last), Step); |
|||
end if; |
|||
end Get_Slide; |
|||
Key: String(1 .. Key_Length); |
|||
int main(int argc, char *argv[]) |
|||
{ |
|||
char const usage[] = "Usage: vinigere [-d] key"; |
|||
char sign = 1; |
|||
char const plainmsg[] = "Plain text: "; |
|||
char const cryptmsg[] = "Cipher text: "; |
|||
bool encrypt = true; |
|||
int opt; |
|||
S: String renames Cryptogram; |
|||
while ((opt = getopt(argc, argv, "d")) != -1) { |
|||
switch (opt) { |
|||
case 'd': |
|||
sign = -1; |
|||
encrypt = false; |
|||
break; |
|||
default: |
|||
fprintf(stderr, "Unrecogized command line argument:'-%i'\n", opt); |
|||
fprintf(stderr, "\n%s\n", usage); |
|||
return 1; |
|||
} |
|||
} |
|||
begin |
|||
if (argc - optind != 1) { |
|||
for I in Key'Range loop |
|||
fprintf(stderr, "%s requires one argument and one only\n", argv[0]); |
|||
Key(I) := Find_Caesar_Key(Get_Slide(S(S'First+I-1 .. S'Last), |
|||
fprintf(stderr, "\n%s\n", usage); |
|||
Key_Length)); |
|||
return 1; |
|||
end loop; |
|||
return Key; |
|||
end Find_Key; |
|||
function Key_Char(Key: String; Index: Positive) return Letter is |
|||
begin |
|||
if Index > Key'Last then |
|||
return Key_Char(Key, Index-Key'Last); |
|||
else |
|||
return Key(Index); |
|||
end if; |
|||
end Key_Char; |
|||
Ciphertext: String := Remove_Whitespace( |
|||
// Convert argument into array of shifts |
|||
"MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH" & |
|||
char const *const restrict key = argv[optind]; |
|||
"VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD" & |
|||
size_t const keylen = strlen(key); |
|||
"ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS" & |
|||
char shifts[keylen]; |
|||
"FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG" & |
|||
"ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ" & |
|||
"ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS" & |
|||
"JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT" & |
|||
"LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST" & |
|||
"MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH" & |
|||
"QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV" & |
|||
"RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW" & |
|||
"TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO" & |
|||
"SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR" & |
|||
"ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX" & |
|||
"BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB" & |
|||
"BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA" & |
|||
"FWAML ZZRXJ EKAHV FASMU LVVUT TGK"); |
|||
Best_Plain: String := Ciphertext; |
|||
char const *restrict plaintext = NULL; |
|||
Best_Dist: Float := Distance(English, Get_Frequency(Best_Plain)); |
|||
for (size_t i = 0; i < keylen; i++) { |
|||
Best_Key: String := Ciphertext; |
|||
if (!(isalpha(key[i]))) { |
|||
Best_Key_L: Natural := 0; |
|||
fprintf(stderr, "Invalid key\n"); |
|||
return 2; |
|||
} |
|||
char const charcase = (isupper(key[i])) ? 'A' : 'a'; |
|||
// If decrypting, shifts will be negative. |
|||
// This line would turn "bacon" into {1, 0, 2, 14, 13} |
|||
shifts[i] = (key[i] - charcase) * sign; |
|||
} |
|||
begin -- Vignere_Cryptanalysis |
|||
do { |
|||
for I in 1 .. Ciphertext'Length/10 loop |
|||
fflush(stdout); |
|||
declare |
|||
// Print "Plain text: " if encrypting and "Cipher text: " if |
|||
Key: String(1 .. I) := Find_Key(Ciphertext, I); |
|||
// decrypting |
|||
Plaintext: String(Ciphertext'Range); |
|||
printf("%s", (encrypt) ? plainmsg : cryptmsg); |
|||
begin |
|||
plaintext = get_input(); |
|||
for I in Ciphertext'Range loop |
|||
Plaintext(I) := Ciphertext(I) - Key_Char(Key, I); |
|||
end loop; |
|||
if Distance(English, Get_Frequency(Plaintext)) < Best_Dist then |
|||
} |
|||
Best_Plain := Plaintext; |
|||
} while (strcmp(plaintext, "") == 0); // Reprompt if entry is empty |
|||
Best_Dist := Distance(English, Get_Frequency(Plaintext)); |
|||
Best_Key(1 .. I) := Key; |
|||
Best_Key_L := I; |
|||
if Best_dist < 0.01 then |
|||
declare |
|||
use Ada.Text_IO; |
|||
begin |
|||
Put_Line("Key =" & Best_Key(1 .. Best_Key_L)); |
|||
Put_Line("Distance = " & Float'Image(Best_Dist)); |
|||
New_Line; |
|||
Put_Line("Plaintext ="); |
|||
Put_Line(Best_Plain); |
|||
New_Line; New_Line; |
|||
end; |
|||
end if; |
|||
end if; |
|||
end; |
|||
end loop; |
|||
end Vignere_Cryptanalysis;</syntaxhighlight> |
|||
=={{header|C}}== |
|||
size_t const plainlen = strlen(plaintext); |
|||
This finds the right key (I think, I didn't try to decode it after getting the key). The program is not fully auto, but by its output, the result is pretty obvious. |
|||
<syntaxhighlight lang="c">#include <stdio.h> |
|||
#include <stdlib.h> |
|||
#include <string.h> |
|||
#include <ctype.h> |
|||
#include <math.h> |
|||
const char *encoded = |
|||
char* const restrict ciphertext = calloc(plainlen + 1, sizeof *ciphertext); |
|||
"MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH" |
|||
if (ciphertext == NULL) { |
|||
"VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD" |
|||
fprintf(stderr, "Memory error\n"); |
|||
"ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS" |
|||
return 5; |
|||
"FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG" |
|||
} |
|||
"ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ" |
|||
"ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS" |
|||
"JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT" |
|||
"LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST" |
|||
"MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH" |
|||
"QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV" |
|||
"RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW" |
|||
"TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO" |
|||
"SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR" |
|||
"ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX" |
|||
"BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB" |
|||
"BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA" |
|||
"FWAML ZZRXJ EKAHV FASMU LVVUT TGK"; |
|||
const double freq[] = { |
|||
for (size_t i = 0, j = 0; i < plainlen; i++) { |
|||
0.08167, 0.01492, 0.02782, 0.04253, 0.12702, 0.02228, 0.02015, |
|||
// Skip non-alphabetical characters |
|||
0.06094, 0.06966, 0.00153, 0.00772, 0.04025, 0.02406, 0.06749, |
|||
if (!(isalpha(plaintext[i]))) { |
|||
0.07507, 0.01929, 0.00095, 0.05987, 0.06327, 0.09056, 0.02758, |
|||
ciphertext[i] = plaintext[i]; |
|||
0.00978, 0.02360, 0.00150, 0.01974, 0.00074 |
|||
continue; |
|||
}; |
|||
int best_match(const double *a, const double *b) { |
|||
double sum = 0, fit, d, best_fit = 1e100; |
|||
int i, rotate, best_rotate = 0; |
|||
for (i = 0; i < 26; i++) |
|||
sum += a[i]; |
|||
for (rotate = 0; rotate < 26; rotate++) { |
|||
fit = 0; |
|||
for (i = 0; i < 26; i++) { |
|||
d = a[(i + rotate) % 26] / sum - b[i]; |
|||
fit += d * d / b[i]; |
|||
} |
|||
if (fit < best_fit) { |
|||
best_fit = fit; |
|||
best_rotate = rotate; |
|||
} |
} |
||
// Check case |
|||
char const charcase = (isupper(plaintext[i])) ? 'A' : 'a'; |
|||
// Wrapping conversion algorithm |
|||
ciphertext[i] = ((plaintext[i] + shifts[j] - charcase + NUMLETTERS) % NUMLETTERS) + charcase; |
|||
j = (j+1) % keylen; |
|||
} |
} |
||
ciphertext[plainlen] = '\0'; |
|||
printf("%s%s\n", (encrypt) ? cryptmsg : plainmsg, ciphertext); |
|||
return best_rotate; |
|||
// Silence warnings about const not being maintained in cast to void* |
|||
free((char*) plaintext); |
|||
return 0; |
|||
} |
} |
||
char *get_input(void) { |
|||
double freq_every_nth(const int *msg, int len, int interval, char *key) { |
|||
char *const restrict buf = malloc(BUFSIZE * sizeof (char)); |
|||
double sum, d, ret; |
|||
double out[26], accu[26] = {0}; |
|||
int i, j, rot; |
|||
for (j = 0; j < interval; j++) { |
|||
for (i = 0; i < 26; i++) |
|||
out[i] = 0; |
|||
for (i = j; i < len; i += interval) |
|||
out[msg[i]]++; |
|||
key[j] = rot = best_match(out, freq); |
|||
key[j] += 'A'; |
|||
for (i = 0; i < 26; i++) |
|||
accu[i] += out[(i + rot) % 26]; |
|||
} |
} |
||
for (i = 0, sum = 0; i < 26; i++) |
|||
fgets(buf, BUFSIZE, stdin); |
|||
sum += accu[i]; |
|||
for (i = 0, ret = 0; i < 26; i++) { |
|||
// Get rid of newline |
|||
d = accu[i] / sum - freq[i]; |
|||
ret += d * d / freq[i]; |
|||
} |
|||
key[interval] = '\0'; |
|||
return ret; |
|||
}</lang> |
|||
} |
|||
int main() { |
|||
{{out}} |
|||
int txt[strlen(encoded)]; |
|||
<pre>$ ./vigenere VIGENERECIPHER |
|||
int len = 0, j; |
|||
Plain text: Beware the Jabberwock, my son! The jaws that bite, the claws that catch! |
|||
char key[100]; |
|||
Cipher text: Wmceei klg Rpifvmeugx, qp wqv! Ioi avey xuek fkbt, alv xtgaf xyev kpagy! |
|||
double fit, best_fit = 1e100; |
|||
for (j = 0; encoded[j] != '\0'; j++) |
|||
$ ./vigenere -d VIGENERECIPHER |
|||
if (isupper(encoded[j])) |
|||
Cipher text: Wmceei klg Rpifvmeugx, qp wqv! Ioi avey xuek fkbt, alv xtgaf xyev kpagy! |
|||
txt[len++] = encoded[j] - 'A'; |
|||
Plain text: Beware the Jabberwock, my son! The jaws that bite, the claws that catch!</pre> |
|||
for (j = 1; j < 30; j++) { |
|||
=={{header|C sharp|C#}}== |
|||
fit = freq_every_nth(txt, len, j, key); |
|||
<lang csharp> |
|||
printf("%f, key length: %2d, %s", fit, j, key); |
|||
using System; |
|||
if (fit < best_fit) { |
|||
best_fit = fit; |
|||
namespace VigenereCipher |
|||
printf(" <--- best so far"); |
|||
{ |
|||
class VCipher |
|||
{ |
|||
public string encrypt(string txt, string pw, int d) |
|||
{ |
|||
int pwi = 0, tmp; |
|||
string ns = ""; |
|||
txt = txt.ToUpper(); |
|||
pw = pw.ToUpper(); |
|||
foreach (char t in txt) |
|||
{ |
|||
if (t < 65) continue; |
|||
tmp = t - 65 + d * (pw[pwi] - 65); |
|||
if (tmp < 0) tmp += 26; |
|||
ns += Convert.ToChar(65 + ( tmp % 26) ); |
|||
if (++pwi == pw.Length) pwi = 0; |
|||
} |
|||
return ns; |
|||
} |
|||
}; |
|||
class Program |
|||
{ |
|||
static void Main(string[] args) |
|||
{ |
|||
VCipher v = new VCipher(); |
|||
string s0 = "Beware the Jabberwock, my son! The jaws that bite, the claws that catch!", |
|||
pw = "VIGENERECIPHER"; |
|||
Console.WriteLine(s0 + "\n" + pw + "\n"); |
|||
string s1 = v.encrypt(s0, pw, 1); |
|||
Console.WriteLine("Encrypted: " + s1); |
|||
s1 = v.encrypt(s1, "VIGENERECIPHER", -1); |
|||
Console.WriteLine("Decrypted: " + s1); |
|||
Console.WriteLine("\nPress any key to continue..."); |
|||
Console.ReadKey(); |
|||
} |
} |
||
printf("\n"); |
|||
} |
} |
||
} |
|||
</lang> |
|||
{{out}} |
|||
<pre> |
|||
Beware the Jabberwock, my son! The jaws that bite, the claws that catch! |
|||
VIGENERECIPHER |
|||
return 0; |
|||
Encrypted: WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY |
|||
}</syntaxhighlight> |
|||
Decrypted: BEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCH |
|||
</pre> |
|||
=={{header|C++}}== |
=={{header|C++}}== |
||
Not guaranteed to give a 100% correct answer, but it works here. Requires C++0x. |
|||
<lang cpp>#include <iostream> |
|||
<syntaxhighlight lang="cpp">#include <iostream> |
|||
#include <string> |
#include <string> |
||
#include <vector> |
|||
#include <map> |
|||
#include <algorithm> |
|||
#include <array> |
|||
using namespace std; |
using namespace std; |
||
typedef array<pair<char, double>, 26> FreqArray; |
|||
class Vigenere |
|||
class VigenereAnalyser |
|||
{ |
{ |
||
private: |
|||
public: |
|||
array<double, 26> targets; |
|||
string key; |
|||
array<double, 26> sortedTargets; |
|||
FreqArray freq; |
|||
// Update the freqs array |
|||
Vigenere(string key) |
|||
FreqArray& frequency(const string& input) |
|||
{ |
{ |
||
for( |
for (char c = 'A'; c <= 'Z'; ++c) |
||
freq[c - 'A'] = make_pair(c, 0); |
|||
{ |
|||
if(key[i] >= 'A' && key[i] <= 'Z') |
|||
for (size_t i = 0; i < input.size(); ++i) |
|||
this->key += key[i]; |
|||
freq[input[i] - 'A'].second++; |
|||
this->key += key[i] + 'A' - 'a'; |
|||
return freq; |
|||
} |
} |
||
double correlation(const string& input) |
|||
{ |
{ |
||
double result = 0.0; |
|||
frequency(input); |
|||
sort(freq.begin(), freq.end(), [](pair<char, double> u, pair<char, double> v)->bool |
|||
for(int i = 0, j = 0; i < text.length(); ++i) |
|||
{ return u.second < v.second; }); |
|||
{ |
|||
char c = text[i]; |
|||
if(c >= 'a' && c <= 'z') |
|||
c += 'A' - 'a'; |
|||
else if(c < 'A' || c > 'Z') |
|||
continue; |
|||
for (size_t i = 0; i < 26; ++i) |
|||
result += freq[i].second * sortedTargets[i]; |
|||
} |
|||
return |
return result; |
||
} |
} |
||
public: |
|||
string decrypt(string text) |
|||
VigenereAnalyser(const array<double, 26>& targetFreqs) |
|||
{ |
{ |
||
targets = targetFreqs; |
|||
sortedTargets = targets; |
|||
sort(sortedTargets.begin(), sortedTargets.end()); |
|||
} |
|||
pair<string, string> analyze(string input) |
|||
for(int i = 0, j = 0; i < text.length(); ++i) |
|||
{ |
|||
string cleaned; |
|||
for (size_t i = 0; i < input.size(); ++i) |
|||
{ |
{ |
||
if (input[i] >= 'A' && input[i] <= 'Z') |
|||
cleaned += input[i]; |
|||
if( |
else if (input[i] >= 'a' && input[i] <= 'z') |
||
cleaned += input[i] + 'A' - 'a'; |
|||
else if(c < 'A' || c > 'Z') |
|||
continue; |
|||
out += (c - key[j] + 26) % 26 + 'A'; |
|||
j = (j + 1) % key.length(); |
|||
} |
} |
||
size_t bestLength = 0; |
|||
double bestCorr = -100.0; |
|||
} |
|||
}; |
|||
// Assume that if there are less than 20 characters |
|||
int main() |
|||
// per column, the key's too long to guess |
|||
{ |
|||
for (size_t i = 2; i < cleaned.size() / 20; ++i) |
|||
Vigenere cipher("VIGENERECIPHER"); |
|||
{ |
|||
vector<string> pieces(i); |
|||
for (size_t j = 0; j < cleaned.size(); ++j) |
|||
pieces[j % i] += cleaned[j]; |
|||
// The correlation increases artificially for smaller |
|||
string original = "Beware the Jabberwock, my son! The jaws that bite, the claws that catch!"; |
|||
// pieces/longer keys, so weigh against them a little |
|||
string encrypted = cipher.encrypt(original); |
|||
double corr = -0.5*i; |
|||
string decrypted = cipher.decrypt(encrypted); |
|||
for (size_t j = 0; j < i; ++j) |
|||
corr += correlation(pieces[j]); |
|||
if (corr > bestCorr) |
|||
cout << original << endl; |
|||
{ |
|||
cout << "Encrypted: " << encrypted << endl; |
|||
bestLength = i; |
|||
cout << "Decrypted: " << decrypted << endl; |
|||
bestCorr = corr; |
|||
}</lang> |
|||
} |
|||
} |
|||
if (bestLength == 0) |
|||
{{out}} |
|||
return make_pair("Text is too short to analyze", ""); |
|||
<pre> |
|||
Beware the Jabberwock, my son! The jaws that bite, the claws that catch! |
|||
Encrypted: WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY |
|||
Decrypted: BEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCH |
|||
</pre> |
|||
vector<string> pieces(bestLength); |
|||
=={{header|Ceylon}}== |
|||
for (size_t i = 0; i < cleaned.size(); ++i) |
|||
<lang ceylon>shared void run() { |
|||
pieces[i % bestLength] += cleaned[i]; |
|||
function normalize(String text) => text.uppercased.filter(Character.letter); |
|||
function crypt(String text, String key, Character(Character, Character) transform) => String { |
|||
for ([a, b] in zipPairs(normalize(text), normalize(key).cycled)) |
|||
transform(a, b) |
|||
}; |
|||
function encrypt(String clearText, String key) => |
|||
crypt(clearText, key, (Character a, Character b) => |
|||
('A'.integer + ((a.integer + b.integer - 130) % 26)).character); |
|||
vector<FreqArray> freqs; |
|||
function decrypt(String cipherText, String key) => |
|||
for (size_t i = 0; i < bestLength; ++i) |
|||
crypt(cipherText, key, (Character a, Character b) => |
|||
freqs.push_back(frequency(pieces[i])); |
|||
('A'.integer + ((a.integer - b.integer + 26) % 26)).character); |
|||
value key = "VIGENERECIPHER"; |
|||
value message = "Beware the Jabberwock, my son! The jaws that bite, the claws that catch!"; |
|||
value encrypted = encrypt(message, key); |
|||
value decrypted = decrypt(encrypted, key); |
|||
print(encrypted); |
|||
print(decrypted); |
|||
}</lang> |
|||
string key = ""; |
|||
=={{header|Clojure}}== |
|||
for (size_t i = 0; i < bestLength; ++i) |
|||
Requires Clojure 1.2. |
|||
{ |
|||
<lang clojure>(ns org.rosettacode.clojure.vigenere |
|||
sort(freqs[i].begin(), freqs[i].end(), [](pair<char, double> u, pair<char, double> v)->bool |
|||
(:require [clojure.string :as string])) |
|||
{ return u.second > v.second; }); |
|||
size_t m = 0; |
|||
; convert letter to offset from \A |
|||
double mCorr = 0.0; |
|||
(defn to-num [char] (- (int char) (int \A))) |
|||
for (size_t j = 0; j < 26; ++j) |
|||
{ |
|||
double corr = 0.0; |
|||
char c = 'A' + j; |
|||
for (size_t k = 0; k < 26; ++k) |
|||
{ |
|||
int d = (freqs[i][k].first - c + 26) % 26; |
|||
corr += freqs[i][k].second * targets[d]; |
|||
} |
|||
if (corr > mCorr) |
|||
; convert number to letter, treating it as modulo 26 offset from \A |
|||
{ |
|||
(defn from-num [num] (char (+ (mod num 26) (int \A)))) |
|||
m = j; |
|||
mCorr = corr; |
|||
} |
|||
} |
|||
key += m + 'A'; |
|||
; Convert a string to a sequence of just the letters as uppercase chars |
|||
} |
|||
(defn to-normalized-seq [str] |
|||
(map #'first (re-seq #"[A-Z]" (string/upper-case str)))) |
|||
; add (op=+) or subtract (op=-) the numerical value of the key letter from the |
|||
; text letter. |
|||
(defn crypt1 [op text key] |
|||
(from-num (apply op (list (to-num text) (to-num key))))) |
|||
string result = ""; |
|||
(defn crypt [op text key] |
|||
for (size_t i = 0; i < cleaned.size(); ++i) |
|||
(let [xcrypt1 (partial #'crypt1 op)] |
|||
result += (cleaned[i] - key[i % key.length()] + 26) % 26 + 'A'; |
|||
(apply #'str |
|||
(map xcrypt1 (to-normalized-seq text) |
|||
(cycle (to-normalized-seq key)))))) |
|||
; encipher a text |
|||
(defn encrypt [plaintext key] (crypt #'+ plaintext key)) |
|||
; decipher a text |
|||
(defn decrypt [ciphertext key] (crypt #'- ciphertext key))</lang> |
|||
return make_pair(result, key); |
|||
Demonstration code: |
|||
} |
|||
<lang clojure>(ns org.rosettacode.clojure.test-vigenere |
|||
}; |
|||
(:require [org.rosettacode.clojure.vigenere :as vigenere])) |
|||
int main() |
|||
(let |
|||
{ |
|||
[ plaintext "Beware the Jabberwock, my son! The jaws that bite, the claws that catch!" |
|||
string input = |
|||
key "Vigenere cipher" |
|||
"MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH" |
|||
ciphertext (vigenere/encrypt plaintext key) |
|||
"VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD" |
|||
recovered (vigenere/decrypt ciphertext key) ] |
|||
"ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS" |
|||
"FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG" |
|||
"ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ" |
|||
"ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS" |
|||
"JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT" |
|||
"LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST" |
|||
"MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH" |
|||
"QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV" |
|||
"RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW" |
|||
"TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO" |
|||
"SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR" |
|||
"ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX" |
|||
"BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB" |
|||
"BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA" |
|||
"FWAML ZZRXJ EKAHV FASMU LVVUT TGK"; |
|||
array<double, 26> english = { |
|||
(doall (map (fn [[k v]] (printf "%9s: %s\n" k v)) |
|||
0.08167, 0.01492, 0.02782, 0.04253, 0.12702, 0.02228, |
|||
[ ["Original" plaintext] ["Key" key] ["Encrypted" ciphertext] ["Decrypted" recovered] ]))) |
|||
0.02015, 0.06094, 0.06966, 0.00153, 0.00772, 0.04025, |
|||
</lang> |
|||
0.02406, 0.06749, 0.07507, 0.01929, 0.00095, 0.05987, |
|||
0.06327, 0.09056, 0.02758, 0.00978, 0.02360, 0.00150, |
|||
0.01974, 0.00074}; |
|||
VigenereAnalyser va(english); |
|||
{{out}} |
|||
pair<string, string> output = va.analyze(input); |
|||
<pre> Original: Beware the Jabberwock, my son! The jaws that bite, the claws that catch! |
|||
Key: Vigenere cipher |
|||
Encrypted: WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY |
|||
Decrypted: BEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCH</pre> |
|||
cout << "Key: " << output.second << endl << endl; |
|||
=={{header|CoffeeScript}}== |
|||
cout << "Text: " << output.first << endl; |
|||
{{trans|D}} |
|||
}</syntaxhighlight> |
|||
<lang coffeescript># Simple helper since charCodeAt is quite long to write. |
|||
code = (char) -> char.charCodeAt() |
|||
encrypt = (text, key) -> |
|||
res = [] |
|||
j = 0 |
|||
for c in text.toUpperCase() |
|||
continue if c < 'A' or c > 'Z' |
|||
res.push ((code c) + (code key[j]) - 130) % 26 + 65 |
|||
j = ++j % key.length |
|||
String.fromCharCode res... |
|||
decrypt = (text, key) -> |
|||
res = [] |
|||
j = 0 |
|||
for c in text.toUpperCase() |
|||
continue if c < 'A' or c > 'Z' |
|||
res.push ((code c) - (code key[j]) + 26) % 26 + 65 |
|||
j = ++j % key.length |
|||
String.fromCharCode res... |
|||
# Trying it out |
|||
key = "VIGENERECIPHER" |
|||
original = "Beware the Jabberwock, my son! The jaws that bite, the claws that catch!" |
|||
encrypted = encrypt original, key |
|||
console.log "Original : #{original}" |
|||
console.log "Encrypted : #{encrypted}" |
|||
console.log "Decrypted : #{decrypt encrypted, key}"</lang> |
|||
<pre>Original : Beware the Jabberwock, my son! The jaws that bite, the claws that catch! |
|||
Encrypted : WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY |
|||
Decrypted : BEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCH</pre> |
|||
=={{header|Common Lisp}}== |
|||
This doesn't assume anything about character codes other than A-Z being a contiguous block (but still, we could be using EBCDIC. Who knows.) |
|||
<lang lisp>(defun strip (s) |
|||
(remove-if-not |
|||
(lambda (c) (char<= #\A c #\Z)) |
|||
(string-upcase s))) |
|||
(defun vigenère (s key &key decipher |
|||
&aux (A (char-code #\A)) |
|||
(op (if decipher #'- #'+))) |
|||
(labels |
|||
((to-char (c) (code-char (+ c A))) |
|||
(to-code (c) (- (char-code c) A))) |
|||
(let ((k (map 'list #'to-code (strip key)))) |
|||
(setf (cdr (last k)) k) |
|||
(map 'string |
|||
(lambda (c) |
|||
(prog1 |
|||
(to-char |
|||
(mod (funcall op (to-code c) (car k)) 26)) |
|||
(setf k (cdr k)))) |
|||
(strip s))))) |
|||
(let* ((msg "Beware the Jabberwock... The jaws that... the claws that catch!") |
|||
(key "vigenere cipher") |
|||
(enc (vigenère msg key)) |
|||
(dec (vigenère enc key :decipher t))) |
|||
(format t "msg: ~a~%enc: ~a~%dec: ~a~%" msg enc dec))</lang> |
|||
{{out}} |
|||
<pre>msg: Beware the Jabberwock... The jaws that... the claws that catch! |
|||
enc: WMCEEIKLGRPIFVMEUGXXYILILZXYVBZLRGCEYAIOEKXIZGU |
|||
dec: BEWARETHEJABBERWOCKTHEJAWSTHATTHECLAWSTHATCATCH</pre> |
|||
=={{header|D}}== |
=={{header|D}}== |
||
{{trans|C++}} |
|||
<lang d>import std.stdio, std.string; |
|||
<syntaxhighlight lang="d">import std.stdio, std.algorithm, std.typecons, std.string, |
|||
std.array, std.numeric, std.ascii; |
|||
string |
string[2] vigenereDecrypt(in double[] targetFreqs, in string input) { |
||
enum nAlpha = std.ascii.uppercase.length; |
|||
in { |
|||
assert(key.removechars("^A-Z") == key); |
|||
} body { |
|||
string res; |
|||
foreach (immutable i, immutable c; txt.toUpper.removechars("^A-Z")) |
|||
res ~= (c + key[i % $] - 2 * 'A') % 26 + 'A'; |
|||
return res; |
|||
} |
|||
static double correlation(in string txt, in double[] sTargets) |
|||
pure nothrow /*@safe*/ @nogc { |
|||
in { |
|||
uint[nAlpha] charCounts = 0; |
|||
assert(key.removechars("^A-Z") == key); |
|||
foreach (immutable c; txt) |
|||
} body { |
|||
charCounts[c - 'A']++; |
|||
string res; |
|||
return charCounts[].sort().release.dotProduct(sTargets); |
|||
foreach (immutable i, immutable c; txt.toUpper.removechars("^A-Z")) |
|||
} |
|||
res ~= (c - key[i % $] + 26) % 26 + 'A'; |
|||
return res; |
|||
} |
|||
static frequency(in string txt) pure nothrow @safe { |
|||
void main() { |
|||
auto freqs = new Tuple!(char,"c", uint,"d")[nAlpha]; |
|||
immutable key = "VIGENERECIPHER"; |
|||
foreach (immutable i, immutable c; std.ascii.uppercase) |
|||
immutable original = "Beware the Jabberwock, my son!" ~ |
|||
freqs[i] = tuple(c, 0); |
|||
foreach (immutable c; txt) |
|||
immutable encoded = original.encrypt(key); |
|||
freqs[c - 'A'].d++; |
|||
writeln(encoded, "\n", encoded.decrypt(key)); |
|||
return freqs; |
|||
}</lang> |
|||
} |
|||
{{out}} |
|||
<pre>WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY |
|||
BEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCH</pre> |
|||
static string[2] decode(in string cleaned, in string key) |
|||
===Alternative Version=== |
|||
pure nothrow @safe { |
|||
{{trans|Perl 6}} |
|||
assert(!key.empty); |
|||
<lang d>import std.stdio, std.range, std.ascii, std.string, std.algorithm, |
|||
string decoded; |
|||
foreach (immutable i, immutable c; cleaned) |
|||
decoded ~= (c - key[i % $] + nAlpha) % nAlpha + 'A'; |
|||
return [key, decoded]; |
|||
} |
|||
static size_t findBestLength(in string cleaned, |
|||
immutable mod = (in int m, in int n) pure nothrow @safe @nogc => |
|||
in double[] sTargets) |
|||
((m % n) + n) % n; |
|||
pure nothrow /*@safe*/ { |
|||
size_t bestLength; |
|||
double bestCorr = -100.0; |
|||
// Assume that if there are less than 20 characters |
|||
immutable _s2v = (in string s) pure /*nothrow*/ @safe => |
|||
// per column, the key's too long to guess |
|||
s.toUpper.removechars("^A-Z").map!q{ a - 'A' }; |
|||
foreach (immutable i; 2 .. cleaned.length / 20) { |
|||
auto pieces = new Appender!string[i]; |
|||
foreach (immutable j, immutable c; cleaned) |
|||
pieces[j % i] ~= c; |
|||
// The correlation seems to increase for smaller |
|||
string _v2s(R)(R v) pure /*nothrow*/ @safe { |
|||
// pieces/longer keys, so weigh against them a little |
|||
return v.map!(x => uppercase[x.mod(26)]).text; |
|||
double corr = -0.5 * i; |
|||
} |
|||
foreach (const p; pieces) |
|||
corr += correlation(p.data, sTargets); |
|||
if (corr > bestCorr) { |
|||
immutable encrypt = (in string txt, in string key) pure /*nothrow*/ @safe => |
|||
bestLength = i; |
|||
txt._s2v.zip(key._s2v.cycle).map!q{ a[0] + a[1] }._v2s; |
|||
bestCorr = corr; |
|||
} |
|||
} |
|||
return bestLength; |
|||
immutable decrypt = (in string txt, in string key) pure /*nothrow*/ @safe => |
|||
txt._s2v.zip(key._s2v.cycle).map!q{ a[0] - a[1] }._v2s; |
|||
void main() { |
|||
immutable key = "Vigenere Cipher!!!"; |
|||
immutable original = "Beware the Jabberwock, my son!" ~ |
|||
" The jaws that bite, the claws that catch!"; |
|||
immutable encoded = original.encrypt(key); |
|||
writeln(encoded, "\n", encoded.decrypt(key)); |
|||
}</lang> |
|||
The output is the same. |
|||
=={{header|Elena}}== |
|||
{{trans|C#}} |
|||
ELENA 4.x : |
|||
<lang elena>import system'text; |
|||
import system'math; |
|||
import system'routines; |
|||
import extensions; |
|||
class VCipher |
|||
{ |
|||
string encrypt(string txt, string pw, int d) |
|||
{ |
|||
auto output := new TextBuilder(); |
|||
int pwi := 0; |
|||
string PW := pw.upperCase(); |
|||
txt.upperCase().forEach:(t) |
|||
{ |
|||
if(t >= $65) |
|||
{ |
|||
int tmp := t.toInt() - 65 + d * (PW[pwi].toInt() - 65); |
|||
if (tmp < 0) |
|||
{ |
|||
tmp += 26 |
|||
}; |
|||
output.write((65 + tmp.mod:26).toChar()); |
|||
pwi += 1; |
|||
if (pwi == PW.Length) { pwi := 0 } |
|||
} |
|||
}; |
|||
^ output.Value |
|||
} |
} |
||
} |
|||
public program() |
|||
{ |
|||
var v := new VCipher(); |
|||
var s0 := "Beware the Jabberwock, my son! The jaws that bite, the claws that catch!"; |
|||
var pw := "VIGENERECIPHER"; |
|||
console.printLine(s0,newLine,pw,newLine); |
|||
var s1 := v.encrypt(s0, pw, 1); |
|||
console.printLine("Encrypted:",s1); |
|||
s1 := v.encrypt(s1, "VIGENERECIPHER", -1); |
|||
console.printLine("Decrypted:",s1); |
|||
console.printLine("Press any key to continue.."); |
|||
console.readChar() |
|||
}</lang> |
|||
{{out}} |
|||
<pre> |
|||
Beware the Jabberwock, my son! The jaws that bite, the claws that catch! |
|||
VIGENERECIPHER |
|||
static string findKey(in string cleaned, in size_t bestLength, |
|||
Encrypted:WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY |
|||
in double[] targetFreqs) pure nothrow @safe { |
|||
Decrypted:BEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCH |
|||
auto pieces = new string[bestLength]; |
|||
Press any key to continue.. |
|||
foreach (immutable i, immutable c; cleaned) |
|||
</pre> |
|||
pieces[i % bestLength] ~= c; |
|||
string key; |
|||
=={{header|Elixir}}== |
|||
foreach (fr; pieces.map!frequency) { |
|||
{{trans|Ruby}} |
|||
fr.sort!q{ a.d > b.d }; |
|||
<lang elixir>defmodule VigenereCipher do |
|||
@base ?A |
|||
@size ?Z - @base + 1 |
|||
def encrypt(text, key), do: crypt(text, key, 1) |
|||
def decrypt(text, key), do: crypt(text, key, -1) |
|||
defp crypt(text, key, dir) do |
|||
text = String.upcase(text) |> String.replace(~r/[^A-Z]/, "") |> to_char_list |
|||
key_iterator = String.upcase(key) |> String.replace(~r/[^A-Z]/, "") |> to_char_list |
|||
|> Enum.map(fn c -> (c - @base) * dir end) |> Stream.cycle |
|||
Enum.zip(text, key_iterator) |
|||
|> Enum.reduce('', fn {char, offset}, ciphertext -> |
|||
[rem(char - @base + offset + @size, @size) + @base | ciphertext] |
|||
end) |
|||
|> Enum.reverse |> List.to_string |
|||
end |
|||
end |
|||
size_t m; |
|||
plaintext = "Beware the Jabberwock, my son! The jaws that bite, the claws that catch!" |
|||
double maxCorr = 0.0; |
|||
key = "Vigenere cipher" |
|||
foreach (immutable j, immutable c; uppercase) { |
|||
ciphertext = VigenereCipher.encrypt(plaintext, key) |
|||
double corr = 0.0; |
|||
recovered = VigenereCipher.decrypt(ciphertext, key) |
|||
foreach (immutable frc; fr) { |
|||
immutable di = (frc.c - c + nAlpha) % nAlpha; |
|||
IO.puts "Original: #{plaintext}" |
|||
corr += frc.d * targetFreqs[di]; |
|||
IO.puts "Encrypted: #{ciphertext}" |
|||
} |
|||
IO.puts "Decrypted: #{recovered}"</lang> |
|||
if (corr > maxCorr) { |
|||
{{out}} |
|||
m = j; |
|||
<pre> |
|||
maxCorr = corr; |
|||
Original: Beware the Jabberwock, my son! The jaws that bite, the claws that catch! |
|||
} |
|||
Encrypted: WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY |
|||
} |
|||
Decrypted: BEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCH |
|||
</pre> |
|||
key ~= m + 'A'; |
|||
=={{header|Erlang}}== |
|||
} |
|||
Erlang is not ideal for string manipulation, but with some utility function definitions it can express this fairly elegantly: |
|||
<lang erlang>% Erlang implementation of Vigenère cipher |
|||
-module(vigenere). |
|||
-export([encrypt/2, decrypt/2]). |
|||
-import(lists, [append/2, filter/2, map/2, zipwith/3]). |
|||
return key; |
|||
% Utility functions for character tests and conversions |
|||
} |
|||
isupper([C|_]) -> isupper(C); |
|||
isupper(C) -> (C >= $A) and (C =< $Z). |
|||
immutable cleaned = input.toUpper.removechars("^A-Z"); |
|||
islower([C|_]) -> islower(C); |
|||
islower(C) -> (C >= $a) and (C =< $z). |
|||
//immutable sortedTargets = targetFreqs.sorted; |
|||
isalpha([C|_]) -> isalpha(C); |
|||
immutable sortedTargets = targetFreqs.dup.sort().release.idup; |
|||
isalpha(C) -> isupper(C) or islower(C). |
|||
immutable bestLength = findBestLength(cleaned, sortedTargets); |
|||
toupper(S) when is_list(S) -> lists:map(fun toupper/1, S); |
|||
if (bestLength == 0) |
|||
toupper(C) when (C >= $a) and (C =< $z) -> C - $a + $A; |
|||
throw new Exception("Text is too short to analyze."); |
|||
toupper(C) -> C. |
|||
immutable string key = findKey(cleaned, bestLength, targetFreqs); |
|||
% modulo function that normalizes into positive range for positive divisor |
|||
return decode(cleaned, key); |
|||
mod(X,Y) -> (X rem Y + Y) rem Y. |
|||
} |
|||
% convert letter to position in alphabet (A=0,B=1,...,Y=24,Z=25). |
|||
to_pos(L) when L >= $A, L =< $Z -> L - $A. |
|||
void main() { |
|||
% convert position in alphabet back to letter |
|||
immutable encoded = "MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG |
|||
from_pos(N) -> mod(N, 26) + $A. |
|||
JSPXY ALUYM NSMYH VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF |
|||
WHTCQ KMLRD ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA |
|||
LWQIS FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG |
|||
ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ ILOVV |
|||
RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS JLAKI FHXUF |
|||
XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT LPRWM JAZPK LQUZA |
|||
ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST MTEOE PAPJH SMFNB YVQUZ |
|||
AALGA YDNMP AQOWT UHDBV TSMUE UIMVH QGVRW AEFSP EMPVE PKXZY WLKJA |
|||
GWALT VYYOB YIXOK IHPDS EVLEV RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY |
|||
IMAPX UOISK PVAGN MZHPW TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV |
|||
YOVDJ SOLXG TGRVO SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV |
|||
GJOKM SIFPR ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO |
|||
ZQDLX BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB |
|||
BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA FWAML |
|||
ZZRXJ EKAHV FASMU LVVUT TGK"; |
|||
immutable englishFrequences = [0.08167, 0.01492, 0.02782, 0.04253, |
|||
% encode the given letter given the single-letter key |
|||
0.12702, 0.02228, 0.02015, 0.06094, 0.06966, 0.00153, 0.00772, |
|||
encipher(P, K) -> from_pos(to_pos(P) + to_pos(K)). |
|||
0.04025, 0.02406, 0.06749, 0.07507, 0.01929, 0.00095, 0.05987, |
|||
0.06327, 0.09056, 0.02758, 0.00978, 0.02360, 0.00150, 0.01974, |
|||
0.00074]; |
|||
immutable key_dec = vigenereDecrypt(englishFrequences, encoded); |
|||
% decode the given letter given the single-letter key |
|||
writefln("Key: %s\n\nText: %s", key_dec[0], key_dec[1]); |
|||
decipher(C, K) -> from_pos(to_pos(C) - to_pos(K)). |
|||
}</syntaxhighlight> |
|||
{{out|Output (cut)}} |
|||
<pre>Key: THECHESHIRECAT |
|||
Text: THISWASTHEPOEMTHATALICEREADJABBERWOCKYTWASBRILLIGANDTHESLITHY...</pre> |
|||
% extend a list by repeating it until it is at least N elements long |
|||
cycle_to(N, List) when length(List) >= N -> List; |
|||
cycle_to(N, List) -> append(List, cycle_to(N-length(List), List)). |
|||
% Encryption prep: reduce string to only its letters, in uppercase |
|||
normalize(Str) -> toupper(filter(fun isalpha/1, Str)). |
|||
crypt(RawText, RawKey, Func) -> |
|||
PlainText = normalize(RawText), |
|||
zipwith(Func, PlainText, cycle_to(length(PlainText), normalize(RawKey))). |
|||
encrypt(Text, Key) -> crypt(Text, Key, fun encipher/2). |
|||
decrypt(Text, Key) -> crypt(Text, Key, fun decipher/2).</lang> |
|||
Demonstration code: |
|||
<lang erlang>-module(testvigenere). |
|||
-import(vigenere,[encrypt/2, decrypt/2]). |
|||
main(_) -> |
|||
Key = "Vigenere cipher", |
|||
CipherText = encrypt("Beware the Jabberwock, my son! The jaws that bite, the claws that catch!", Key), |
|||
RecoveredText = decrypt(CipherText, Key), |
|||
io:fwrite("Ciphertext: ~s~nDecrypted: ~s~n", [CipherText, RecoveredText]).</lang> |
|||
{{out}} |
|||
<pre>Ciphertext: WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY |
|||
Decrypted: BEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCH</pre> |
|||
=={{header|F_Sharp|F#}}== |
|||
<lang fsharp> |
|||
module vigenere = |
|||
let keyschedule (key:string) = |
|||
let s = key.ToUpper().ToCharArray() |> Array.filter System.Char.IsLetter |
|||
let l = Array.length s |
|||
(fun n -> int s.[n % l]) |
|||
let enc k c = ((c + k - 130) % 26) + 65 |
|||
let dec k c = ((c - k + 130) % 26) + 65 |
|||
let crypt f key = Array.mapi (fun n c -> f (key n) c |> char) |
|||
let encrypt key (plaintext:string) = |
|||
plaintext.ToUpper().ToCharArray() |
|||
|> Array.filter System.Char.IsLetter |
|||
|> Array.map int |
|||
|> crypt enc (keyschedule key) |
|||
|> (fun a -> new string(a)) |
|||
let decrypt key (ciphertext:string) = |
|||
ciphertext.ToUpper().ToCharArray() |
|||
|> Array.map int |
|||
|> crypt dec (keyschedule key) |
|||
|> (fun a -> new string(a)) |
|||
let passwd = "Vigenere Cipher" |
|||
let cipher = vigenere.encrypt passwd "Beware the Jabberwock, my son! The jaws that bite, the claws that catch!" |
|||
let plain = vigenere.decrypt passwd cipher |
|||
printfn "%s\n%s" cipher plain |
|||
</lang> |
|||
<pre>C:\src\fsharp>fsi vigenere.fsx |
|||
WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY |
|||
BEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCH |
|||
</pre> |
|||
=={{header|Factor}}== |
|||
<lang>USING: arrays ascii formatting kernel math math.functions |
|||
math.order sequences ; |
|||
IN: rosetta-code.vigenere-cipher |
|||
: mult-pad ( key input -- x ) |
|||
[ length ] bi@ 2dup < [ swap ] when / ceiling ; |
|||
: lengthen-pad ( key input -- rep-key input ) |
|||
[ mult-pad ] 2keep [ <repetition> concat ] dip |
|||
[ length ] keep [ head ] dip ; |
|||
: normalize ( str -- only-upper-letters ) |
|||
>upper [ LETTER? ] filter ; |
|||
: vigenere-encrypt ( key input -- ecrypted ) |
|||
[ normalize ] bi@ lengthen-pad |
|||
[ [ CHAR: A - ] map ] bi@ [ + 26 mod CHAR: A + ] 2map ; |
|||
: vigenere-decrypt ( key input -- decrypted ) |
|||
[ normalize ] bi@ lengthen-pad [ [ CHAR: A - ] map ] bi@ |
|||
[ - 26 - abs 26 mod CHAR: A + ] 2map ; |
|||
: main ( -- ) |
|||
"Vigenere cipher" dup |
|||
"Beware the Jabberwock, my son! The jaws that bite, the claws that catch!" |
|||
2dup "Key: %s\nInput: %s\n" printf |
|||
vigenere-encrypt dup "Encrypted: %s\n" printf |
|||
vigenere-decrypt "Decrypted: %s\n" printf ; |
|||
MAIN: main</lang> |
|||
{{out}} |
|||
<pre> |
|||
Key: Vigenere cipher |
|||
Input: Beware the Jabberwock, my son! The jaws that bite, the claws that catch! |
|||
Encrypted: WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY |
|||
Decrypted: BEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCH |
|||
</pre> |
|||
=={{header|Fortran}}== |
|||
{{works with|Fortran|95 and later}} |
|||
<lang fortran>program vigenere_cipher |
|||
implicit none |
|||
character(80) :: plaintext = "Beware the Jabberwock, my son! The jaws that bite, the claws that catch!", & |
|||
ciphertext = "" |
|||
character(14) :: key = "VIGENERECIPHER" |
|||
call encrypt(plaintext, ciphertext, key) |
|||
write(*,*) plaintext |
|||
write(*,*) ciphertext |
|||
call decrypt(ciphertext, plaintext, key) |
|||
write(*,*) plaintext |
|||
contains |
|||
subroutine encrypt(intxt, outtxt, k) |
|||
character(*), intent(in) :: intxt, k |
|||
character(*), intent(out) :: outtxt |
|||
integer :: chrn |
|||
integer :: cp = 1, kp = 1 |
|||
integer :: i |
|||
outtxt = "" |
|||
do i = 1, len(trim(intxt)) |
|||
select case(intxt(i:i)) |
|||
case ("A":"Z", "a":"z") |
|||
select case(intxt(i:i)) |
|||
case("a":"z") |
|||
chrn = iachar(intxt(i:i)) - 32 |
|||
case default |
|||
chrn = iachar(intxt(i:i)) |
|||
end select |
|||
outtxt(cp:cp) = achar(modulo(chrn + iachar(k(kp:kp)), 26) + 65) |
|||
cp = cp + 1 |
|||
kp = kp + 1 |
|||
if(kp > len(k)) kp = kp - len(k) |
|||
end select |
|||
end do |
|||
end subroutine |
|||
subroutine decrypt(intxt, outtxt, k) |
|||
character(*), intent(in) :: intxt, k |
|||
character(*), intent(out) :: outtxt |
|||
integer :: chrn |
|||
integer :: cp = 1, kp = 1 |
|||
integer :: i |
|||
outtxt = "" |
|||
do i = 1, len(trim(intxt)) |
|||
chrn = iachar(intxt(i:i)) |
|||
outtxt(cp:cp) = achar(modulo(chrn - iachar(k(kp:kp)), 26) + 65) |
|||
cp = cp + 1 |
|||
kp = kp + 1 |
|||
if(kp > len(k)) kp = kp - len(k) |
|||
end do |
|||
end subroutine |
|||
end program</lang> |
|||
{{out}} |
|||
<pre> Beware the Jabberwock, my son! The jaws that bite, the claws that catch! |
|||
WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY |
|||
BEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCH</pre> |
|||
=={{header|Go}}== |
=={{header|Go}}== |
||
{{trans|Kotlin}} |
|||
<lang go>package main |
|||
<syntaxhighlight lang="go">package main |
|||
import |
import ( |
||
"fmt" |
|||
"strings" |
|||
) |
|||
var encoded = |
|||
type vkey string |
|||
"MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH" + |
|||
"VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD" + |
|||
"ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS" + |
|||
"FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG" + |
|||
"ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ" + |
|||
"ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS" + |
|||
"JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT" + |
|||
"LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST" + |
|||
"MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH" + |
|||
"QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV" + |
|||
"RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW" + |
|||
"TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO" + |
|||
"SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR" + |
|||
"ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX" + |
|||
"BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB" + |
|||
"BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA" + |
|||
"FWAML ZZRXJ EKAHV FASMU LVVUT TGK" |
|||
var freq = [26]float64{ |
|||
func newVigenère(key string) (vkey, bool) { |
|||
0.08167, 0.01492, 0.02782, 0.04253, 0.12702, 0.02228, 0.02015, |
|||
v := vkey(upperOnly(key)) |
|||
0.06094, 0.06966, 0.00153, 0.00772, 0.04025, 0.02406, 0.06749, |
|||
return v, len(v) > 0 // key length 0 invalid |
|||
0.07507, 0.01929, 0.00095, 0.05987, 0.06327, 0.09056, 0.02758, |
|||
0.00978, 0.02360, 0.00150, 0.01974, 0.00074, |
|||
} |
} |
||
func ( |
func sum(a []float64) (sum float64) { |
||
for _, f := range a { |
|||
sum += f |
|||
ct[i] = 'A' + (c-'A'+k[i%len(k)]-'A')%26 |
|||
} |
} |
||
return |
return |
||
} |
} |
||
func bestMatch(a []float64) int { |
|||
func (k vkey) decipher(ct string) (string, bool) { |
|||
sum := sum(a) |
|||
bestFit, bestRotate := 1e100, 0 |
|||
for rotate := 0; rotate < 26; rotate++ { |
|||
fit := 0.0 |
|||
for i := 0; i < 26; i++ { |
|||
d := a[(i+rotate)%26]/sum - freq[i] |
|||
fit += d * d / freq[i] |
|||
} |
|||
if fit < bestFit { |
|||
bestFit, bestRotate = fit, rotate |
|||
} |
} |
||
pt[i] = 'A' + (c-k[i%len(k)]+26)%26 |
|||
} |
} |
||
return |
return bestRotate |
||
} |
} |
||
func freqEveryNth(msg []int, key []byte) float64 { |
|||
// upperOnly extracts letters A-Z, a-z from a string and |
|||
l := len(msg) |
|||
// returns them all upper case in a byte slice. |
|||
interval := len(key) |
|||
// Useful for vkey constructor and encipher function. |
|||
out := make([]float64, 26) |
|||
func upperOnly(s string) []byte { |
|||
accu := make([]float64, 26) |
|||
for |
for j := 0; j < interval; j++ { |
||
for k := 0; k < 26; k++ { |
|||
out[k] = 0.0 |
|||
} |
|||
for i := j; i < l; i += interval { |
|||
out[msg[i]]++ |
|||
} |
|||
rot := bestMatch(out) |
|||
key[j] = byte(rot + 65) |
|||
for i := 0; i < 26; i++ { |
|||
accu[i] += out[(i+rot)%26] |
|||
} |
} |
||
} |
} |
||
sum := sum(accu) |
|||
ret := 0.0 |
|||
for i := 0; i < 26; i++ { |
|||
d := accu[i]/sum - freq[i] |
|||
ret += d * d / freq[i] |
|||
} |
|||
return ret |
|||
} |
} |
||
func decrypt(text, key string) string { |
|||
const testKey = "Vigenère Cipher" |
|||
var sb strings.Builder |
|||
const testPT = `Beware the Jabberwock, my son! |
|||
ki := 0 |
|||
The jaws that bite, the claws that catch!` |
|||
for _, c := range text { |
|||
if c < 'A' || c > 'Z' { |
|||
continue |
|||
} |
|||
ci := (c - rune(key[ki]) + 26) % 26 |
|||
sb.WriteRune(ci + 65) |
|||
ki = (ki + 1) % len(key) |
|||
} |
|||
return sb.String() |
|||
} |
|||
func main() { |
func main() { |
||
enc := strings.Replace(encoded, " ", "", -1) |
|||
txt := make([]int, len(enc)) |
|||
for i := 0; i < len(txt); i++ { |
|||
if !ok { |
|||
txt[i] = int(enc[i] - 'A') |
|||
return |
|||
} |
} |
||
bestFit, bestKey := 1e100, "" |
|||
fmt.Println("Effective key:", v) |
|||
fmt.Println(" |
fmt.Println(" Fit Length Key") |
||
for j := 1; j <= 26; j++ { |
|||
key := make([]byte, j) |
|||
fmt.Println("Enciphered:", ct) |
|||
fit := freqEveryNth(txt, key) |
|||
sKey := string(key) |
|||
if !ok { |
|||
fmt. |
fmt.Printf("%f %2d %s", fit, j, sKey) |
||
if fit < bestFit { |
|||
bestFit, bestKey = fit, sKey |
|||
fmt.Print(" <--- best so far") |
|||
} |
|||
fmt.Println() |
|||
} |
} |
||
fmt.Println(" |
fmt.Println("\nBest key :", bestKey) |
||
fmt.Printf("\nDecrypted text:\n%s\n", decrypt(enc, bestKey)) |
|||
}</lang> |
|||
}</syntaxhighlight> |
|||
{{out}} |
{{out}} |
||
Note: carriage returns inserted into decrypted text after every 80 characters to make it more readable. |
|||
<pre> |
<pre> |
||
Fit Length Key |
|||
Supplied key: Vigenère Cipher |
|||
2.984348 1 E <--- best so far |
|||
Effective key: VIGENRECIPHER |
|||
2.483684 2 EC <--- best so far |
|||
Plain text: Beware the Jabberwock, my son! |
|||
2.642487 3 TEE |
|||
The jaws that bite, the claws that catch! |
|||
1.976651 4 THEC <--- best so far |
|||
Enciphered: WMCEEVXJMYHFSZZCSPBQAADUXYZRGAFKLCBQPXVOPKGYRAUBWHXTVBIL |
|||
2.356881 5 EEEPU |
|||
Deciphered: BEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCH |
|||
2.203129 6 TCECEC |
|||
1.051163 7 THECSAS <--- best so far |
|||
1.645763 8 TJQGAHET |
|||
2.001380 9 VEIZSEGNT |
|||
1.824476 10 ECEGAWQTDS |
|||
1.623083 11 TNLUSRXPTAJ |
|||
1.253527 12 XLECTHQGTHEC |
|||
1.399037 13 LJJTDGFNOTENR |
|||
0.152370 14 THECHESHIRECAT <--- best so far |
|||
1.533951 15 JNTOOEEXFTGQTNH |
|||
1.068182 16 TJTSAEETEXHPXHNE |
|||
1.034093 17 AZRAXUHEJLREEXIEE |
|||
1.443345 18 VNIZQPALEPTSXSEXUC |
|||
1.090977 19 FUCAITCSLVTEZDUDEHS |
|||
0.979868 20 EQXGAHWTTQECEWUGXHPI |
|||
0.789410 21 HVRCSAFTHEBDLSTAERSES |
|||
0.881380 22 TVIJTCIGKAQPELECRXPTNC |
|||
0.952456 23 KKEQXGPWTCQEELIEHXUWASV |
|||
0.715968 24 ELAIXHQTTIEDXJETTNTGAEPC |
|||
0.891258 25 OTJUUEGERDNQTUQEAGWUTIEOA |
|||
0.852784 26 IGITEGECAGAVUNLJAHASAVTETW |
|||
Best key : THECHESHIRECAT |
|||
Decrypted text: |
|||
THISWASTHEPOEMTHATALICEREADJABBERWOCKYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIMB |
|||
LEINTHEWABEALLMIMSYWERETHEBOROGOVESANDTHEMOMERATHSOUTGRABEBEWARETHEJABBERWOCKMYS |
|||
ONTHEJAWSTHATBITETHECLAWSTHATCATCHBEWARETHEJUBJUBBIRDANDSHUNTHEFRUMIOUSBANDERSNA |
|||
TCHHETOOKHISVORPALSWORDINHANDLONGTIMETHEMANXOMEFOEHESOUGHTSORESTEDHEBYTHETUMTUMT |
|||
REEANDSTOODAWHILEINTHOUGHTANDASINUFFISHTHOUGHTHESTOODTHEJABBERWOCKWITHEYESOFFLAM |
|||
ECAMEWHIFFLINGTHROUGHTHETULGEYWOODANDBURBLEDASITCAMEONETWOONETWOANDTHROUGHANDTHR |
|||
OUGHTHEVORPALBLADEWENTSNICKERSNACKHELEFTITDEADANDWITHITSHEADHEWENTGALUMPHINGBACK |
|||
ANDHASTTHOUSLAINTHEJABBERWOCKCOMETOMYARMSMYBEAMISHBOYOFRABJOUSDAYCALLOOHCALLAYHE |
|||
CHORTLEDINHISJOYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIMBLEINTHEWABEALLMIMSYWER |
|||
ETHEBOROGOVESANDTHEMOMERATHSOUTGRABEITSEEMSVERYPRETTYSHESAIDWHENSHEHADFINISHEDIT |
|||
BUTITSRATHERHARDTOUNDERSTAND |
|||
</pre> |
</pre> |
||
=={{header|Haskell}}== |
=={{header|Haskell}}== |
||
<syntaxhighlight lang="haskell">{-# LANGUAGE TupleSections #-} |
|||
<lang haskell>import Data.Char |
|||
import Data.List(transpose, nub, sort, maximumBy) |
|||
import Text.Printf |
|||
import Data.Ord (comparing) |
|||
import Data.Char (ord) |
|||
import Data.Map (Map, fromListWith, toList, findWithDefault) |
|||
average :: Fractional a => [a] -> a |
|||
-- Perform encryption or decryption, depending on f. |
|||
average as = sum as / fromIntegral (length as) |
|||
crypt f key = map toLetter . zipWith f (cycle key) |
|||
where toLetter = chr . (+) (ord 'A') |
|||
-- Create a map from each entry in list to the number of occurrences of |
|||
-- Encrypt or decrypt one letter. |
|||
-- that entry in the list. |
|||
enc k c = (ord k + ord c) `mod` 26 |
|||
countEntries :: Ord a => [a] -> Map a Int |
|||
dec k c = (ord c - ord k) `mod` 26 |
|||
countEntries = fromListWith (+) . fmap (,1) |
|||
-- |
-- Break a string up into substrings of n chars. |
||
breakup :: Int -> [a] -> [[a]] |
|||
encrypt = crypt enc |
|||
breakup _ [] = [] |
|||
decrypt = crypt dec |
|||
breakup n as = |
|||
let (h, r) = splitAt n as |
|||
in h:breakup n r |
|||
-- |
-- Dole out elements of a string over a n element distribution. |
||
distribute :: [a] -> Int -> [[a]] |
|||
convert = map toUpper . filter isLetter |
|||
distribute as n = transpose $ breakup n as |
|||
-- The probability that members of a pair of characters taken randomly |
|||
-- from a given string are equal. |
|||
coincidence :: (Ord a, Fractional b) => [a] -> b |
|||
coincidence str = |
|||
let charCounts = snd <$> toList (countEntries str) |
|||
strln = length str |
|||
d = fromIntegral $ strln * (strln - 1) |
|||
n = fromIntegral $ sum $ fmap (\cc -> cc * (cc-1)) charCounts |
|||
in n / d |
|||
-- Use the average probablity of coincidence for all the members of |
|||
-- a distribution to rate the distribution - the higher the better. |
|||
-- The correlation increases artificially for smaller |
|||
-- pieces/longer keys, so weigh against them a little |
|||
rate :: (Ord a, Fractional b) => [[a]] -> b |
|||
rate d = average (fmap coincidence d) - fromIntegral (length d) / 3000.0 |
|||
-- Multiply elements of lists together and add up the results. |
|||
dot :: Num a => [a] -> [a] -> a |
|||
dot v0 v1 = sum $ zipWith (*) v0 v1 |
|||
-- Given two lists of floats, rotate one of them by the number of |
|||
-- characters indicated by letter and then 'dot' them together. |
|||
rotateAndDot :: Num a => [a] -> [a] -> Char -> a |
|||
rotateAndDot v0 v1 letter = dot v0 (drop (ord letter - ord 'A') (cycle v1)) |
|||
-- Find decoding offset that results in best match |
|||
-- between actual char frequencies and expected frequencies. |
|||
getKeyChar :: RealFrac a => [a] -> String -> Char |
|||
getKeyChar expected sample = |
|||
let charCounts = countEntries sample |
|||
countInSample c = findWithDefault 0 c charCounts |
|||
actual = fmap (fromIntegral . countInSample) ['A'..'Z'] |
|||
in maximumBy (comparing $ rotateAndDot expected actual) ['A'..'Z'] |
|||
main = do |
main = do |
||
let |
let cr = filter (/=' ') crypt |
||
-- Assume that if there are less than 20 characters |
|||
-- per column, the key's too long to guess |
|||
distributions = fmap (distribute cr) [1..length cr `div` 20] |
|||
encr = encrypt key $ convert text |
|||
bestDistribution = maximumBy (comparing rate) distributions |
|||
decr = decrypt key encr |
|||
key = fmap (getKeyChar englishFrequencies) bestDistribution |
|||
printf " Input: %s\n Key: %s\nEncrypted: %s\nDecrypted: %s\n" |
|||
alphaSum a b = ['A'..'Z'] !! ((ord b - ord a) `mod` 26) |
|||
text key encr decr</lang> |
|||
mapM_ putStrLn ["Key: " ++ key, "Decrypted Text: " ++ zipWith alphaSum (cycle key) cr] |
|||
englishFrequencies = |
|||
[ 0.08167, 0.01492, 0.02782, 0.04253, |
|||
0.12702, 0.02228, 0.02015, 0.06094, |
|||
0.06966, 0.00153, 0.00772, 0.04025, |
|||
0.02406, 0.06749, 0.07507, 0.01929, |
|||
0.00095, 0.05987, 0.06327, 0.09056, |
|||
0.02758, 0.00978, 0.02360, 0.00150, |
|||
0.01974, 0.00074 ] |
|||
crypt = "\ |
|||
\MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH\ |
|||
\VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD\ |
|||
\ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS\ |
|||
\FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG\ |
|||
\ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ\ |
|||
\ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS\ |
|||
\JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT\ |
|||
\LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST\ |
|||
\MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH\ |
|||
\QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV\ |
|||
\RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW\ |
|||
\TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO\ |
|||
\SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR\ |
|||
\ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX\ |
|||
\BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB\ |
|||
\BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA\ |
|||
\FWAML ZZRXJ EKAHV FASMU LVVUT TGK\ |
|||
\"</syntaxhighlight> |
|||
{{out}} |
{{out}} |
||
<pre style="font-size:80%"> |
|||
<pre> |
|||
Key: THECHESHIRECAT |
|||
Input: Beware the Jabberwock, my son! The jaws that bite, the claws that catch! |
|||
Decrypted Text: THISWASTHEPOEMTHATALICEREADJABBERWOCKYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIMBLEINTHEWABEALLMIMSYWERETHEBOROGOVESANDTHEMOMERATHSOUTGRABEBEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCHBEWARETHEJUBJUBBIRDANDSHUNTHEFRUMIOUSBANDERSNATCHHETOOKHISVORPALSWORDINHANDLONGTIMETHEMANXOMEFOEHESOUGHTSORESTEDHEBYTHETUMTUMTREEANDSTOODAWHILEINTHOUGHTANDASINUFFISHTHOUGHTHESTOODTHEJABBERWOCKWITHEYESOFFLAMECAMEWHIFFLINGTHROUGHTHETULGEYWOODANDBURBLEDASITCAMEONETWOONETWOANDTHROUGHANDTHROUGHTHEVORPALBLADEWENTSNICKERSNACKHELEFTITDEADANDWITHITSHEADHEWENTGALUMPHINGBACKANDHASTTHOUSLAINTHEJABBERWOCKCOMETOMYARMSMYBEAMISHBOYOFRABJOUSDAYCALLOOHCALLAYHECHORTLEDINHISJOYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIMBLEINTHEWABEALLMIMSYWERETHEBOROGOVESANDTHEMOMERATHSOUTGRABEITSEEMSVERYPRETTYSHESAIDWHENSHEHADFINISHEDITBUTITSRATHERHARDTOUNDERSTAND |
|||
Key: VIGENERECIPHER |
|||
Encrypted: WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY |
|||
Decrypted: BEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCH |
|||
</pre> |
</pre> |
||
=={{header| |
=={{header|J}}== |
||
<lang Icon>procedure main() |
|||
ptext := "Beware the Jabberwock, my son! The jaws that bite, the claws that catch!" |
|||
write("Key = ",ekey := "VIGENERECIPHER") |
|||
write("Plain Text = ",ptext) |
|||
write("Normalized = ",GFormat(ptext := NormalizeText(ptext))) |
|||
write("Enciphered = ",GFormat(ctext := Vignere("e",ekey,ptext))) |
|||
write("Deciphered = ",GFormat(ptext := Vignere("d",ekey,ctext))) |
|||
end |
|||
Implementation: |
|||
procedure Vignere(mode,ekey,ptext,alpha) #: Vignere cipher |
|||
<syntaxhighlight lang=J>NB. https://en.wikipedia.org/wiki/Kasiski_examination |
|||
/alpha := &ucase # default |
|||
kasiski=: {{ |
|||
if *alpha ~= *cset(alpha) then runerr(205,alpha) # no dups |
|||
grams=. ({: #"1~1 < ;@{.)|:(#/.~;"0~.) g=. 3 <\ y |
|||
alpha ||:= alpha # unobstructed |
|||
deltas=. ;grams (2 -~/\ I.@E.)L:0 enc |
|||
{:,{.\:~(#/.~,.~.)1 -.~,+./~ deltas |
|||
}} |
|||
NB. https://en.wikipedia.org/wiki/Letter_frequency |
|||
every ctext:="" & p:=ptext[i := 1 to *ptext] & k:=ekey[(i-1)%*ekey+1] do |
|||
AZ=: 8 u: 65+i.26 |
|||
case mode of { |
|||
lfreq=: 0.01*do{{)n |
|||
"e"|"encrypt": |
|||
8.2 1.5 2.8 4.3 13 2.2 2 6.1 7 0.15 |
|||
ctext||:=map(p,alpha[1+:*alpha/2],alpha[find(k,alpha)+:(*alpha/2)]) |
|||
0.77 4 2.4 6.7 7.5 1.9 0.095 6 6.3 9.1 |
|||
"d"|"decrypt": |
|||
2.8 0.98 2.4 0.15 2 0.074 |
|||
ctext||:=map(p,alpha[find(k,alpha)+:(*alpha/2)],alpha[1+:*alpha/2]) |
|||
}}-.LF |
|||
default: runerr(205,mode) |
|||
} |
|||
return ctext |
|||
end</lang> |
|||
The following helper procedures will be of general use with classical cryptography tasks. |
|||
<lang Icon> |
|||
link strings |
|||
caesarkey=: {{ |
|||
procedure NormalizeText(ptext,alpha) #: text/case classical crypto helper |
|||
freqs=. (<:#/.~AZ,y)%#y=. y ([-.-.) AZ |
|||
/alpha := &ucase # default |
|||
AZ{~(i. <./)lfreq +/&.:*:@:-"1 (i.26)|."0 1 freqs |
|||
if &lcase === (alpha := cset(alpha)) then ptext := map(ptext) # lower |
|||
}} |
|||
if &ucase === alpha then ptext := map(ptext,&lcase,&ucase) # upper |
|||
vigenerekey=: {{ caesarkey"1|:(-kasiski y) ]\y }} |
|||
return deletec(ptext,&cset--alpha) # only alphas |
|||
end |
|||
uncaesar=: {{ 26&|@-&(AZ i.x)&.(AZ&i.) y }}"0 1 |
|||
procedure GFormat(text) #: 5 letter group formatting helper |
|||
unvigenere=: {{ ' '-.~,x uncaesar"0 1&.|:(-#x) ]\y }}</syntaxhighlight> |
|||
text ? (s := "", until pos(0) do s ||:= " " || move(5)|tab(0)) |
|||
return s[2:0] |
|||
end</lang> |
|||
Here, kasiski finds all 3-grams (sequences of three adjacent letters) which appear more than once, finds all of the distances between nearest pairs of these sequences, and then further pairs each of these distances with all other distances, finding the greatest common divisor of those distance pairs. Finally, these LCDs are ordered by how many times they appear and the most frequent LCD is taken as the kasiski result. |
|||
{{libheader|Icon Programming Library}} |
|||
[http://www.cs.arizona.edu/icon/library/src/procs/strings.icn strings.icn provides deletec] |
|||
uncaesar works by finding the frequency of occurrence of each letter of the alphabet (in alphabetical order), and then each of the 26 rotations of that sequence are compared with a text frequency alphabet (obtained from a wikipedia table). The rotation with the least root-mean-square sum of differences is chosen as the correct location, and its index is reported as a letter of the alphabet (0=A, 1=B, etc.) |
|||
{{out}} |
|||
<pre>Key = VIGENERECIPHER |
|||
Plain Text = Beware the Jabberwock, my son! The jaws that bite, the claws that catch! |
|||
Normalized = BEWAR ETHEJ ABBER WOCKM YSONT HEJAW STHAT BITET HECLA WSTHA TCATCH |
|||
Enciphered = WMCEE IKLGR PIFVM EUGXQ PWQVI OIAVE YXUEK FKBTA LVXTG AFXYE VKPAGY |
|||
Deciphered = BEWAR ETHEJ ABBER WOCKM YSONT HEJAW STHAT BITET HECLA WSTHA TCATCH</pre> |
|||
(And, the length provided by kasiski is used to break out the sequences to be analyzed by uncaesar...) |
|||
=={{header|J}}== |
|||
'''Solution:'''<br> |
|||
Using <code>vig</code> from the [[j:Addons/convert/misc/vig|convert/misc/vig addon]]: |
|||
<lang j>NB.*vig c Vigenère cipher |
|||
NB. cipher=. key 0 vig charset plain |
|||
NB. plain=. key 1 vig charset cipher |
|||
vig=: conjunction define |
|||
: |
|||
r=. (#y) $ n i.x |
|||
n {~ (#n) | (r*_1^m) + n i.y |
|||
) |
|||
Task example: |
|||
ALPHA=: (65,:26) ];.0 a. NB. Character Set |
|||
preprocess=: (#~ e.&ALPHA)@toupper NB. force uppercase and discard non-alpha chars |
|||
<syntaxhighlight lang=J>enc=: {{)n |
|||
vigEncryptRC=: 0 vig ALPHA preprocess |
|||
MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH |
|||
vigDecryptRC=: 1 vig ALPHA preprocess</lang> |
|||
VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD |
|||
ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS |
|||
'''Example Use:''' |
|||
FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG |
|||
<lang j> 'VIGENERECIPHER' vigEncryptRC 'Beware the Jabberwock, my son! The jaws that bite, the claws that catch!' |
|||
ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ |
|||
WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY |
|||
ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS |
|||
'VIGENERECIPHER' vigDecryptRC 'WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY' |
|||
JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT |
|||
BEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCH</lang> |
|||
LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST |
|||
MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH |
|||
QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV |
|||
RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW |
|||
TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO |
|||
SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR |
|||
ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX |
|||
BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB |
|||
BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA |
|||
FWAML ZZRXJ EKAHV FASMU LVVUT TGK |
|||
}}-.LF,' ' |
|||
vigenerekey enc |
|||
THECHESHIRECAT |
|||
_80]\'THECHESHIRECAT' unvigenere enc |
|||
THISWASTHEPOEMTHATALICEREADJABBERWOCKYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIMB |
|||
LEINTHEWABEALLMIMSYWERETHEBOROGOVESANDTHEMOMERATHSOUTGRABEBEWARETHEJABBERWOCKMYS |
|||
ONTHEJAWSTHATBITETHECLAWSTHATCATCHBEWARETHEJUBJUBBIRDANDSHUNTHEFRUMIOUSBANDERSNA |
|||
TCHHETOOKHISVORPALSWORDINHANDLONGTIMETHEMANXOMEFOEHESOUGHTSORESTEDHEBYTHETUMTUMT |
|||
REEANDSTOODAWHILEINTHOUGHTANDASINUFFISHTHOUGHTHESTOODTHEJABBERWOCKWITHEYESOFFLAM |
|||
ECAMEWHIFFLINGTHROUGHTHETULGEYWOODANDBURBLEDASITCAMEONETWOONETWOANDTHROUGHANDTHR |
|||
OUGHTHEVORPALBLADEWENTSNICKERSNACKHELEFTITDEADANDWITHITSHEADHEWENTGALUMPHINGBACK |
|||
ANDHASTTHOUSLAINTHEJABBERWOCKCOMETOMYARMSMYBEAMISHBOYOFRABJOUSDAYCALLOOHCALLAYHE |
|||
CHORTLEDINHISJOYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIMBLEINTHEWABEALLMIMSYWER |
|||
ETHEBOROGOVESANDTHEMOMERATHSOUTGRABEITSEEMSVERYPRETTYSHESAIDWHENSHEHADFINISHEDIT |
|||
BUTITSRATHERHARDTOUNDERSTANDWYTWITSJWYAH |
|||
</syntaxhighlight> |
|||
As an aside, note that we could go directly from encrypted text to decrypted text, without showing the key. For example, using: |
|||
<syntaxhighlight lang=J>decaesar=: {{ |
|||
freqs=. (<:#/.~AZ,y)%#y=. y ([-.-.) AZ |
|||
ndx=. (i. <./)lfreq +/&.:*:@:-"1 (i.26)|."0 1 freqs |
|||
26&|@-&ndx&.(AZ&i.) y |
|||
}} |
|||
devigenere=: {{ ' '-.~,decaesar"1&.|:(-kasiski y) ]\y }}</syntaxhighlight> |
|||
That said, it's also worth noting that noise issues mean that if this were to be used in practical contexts the approach should instead be to expose more intermediate results, rather than less, with a special focus on the representations of frequency distributions (here, we're always picking the first alternative, but it's vaguely plausible that a different alternative might actually be useful in some cases). |
|||
=={{header|Java}}== |
=={{header|Java}}== |
||
{{trans| |
{{trans|C}} |
||
<lang java>public class VigenereCipher { |
|||
public static void main(String[] args) { |
|||
String key = "VIGENERECIPHER"; |
|||
String ori = "Beware the Jabberwock, my son! The jaws that bite, the claws that catch!"; |
|||
String enc = encrypt(ori, key); |
|||
System.out.println(enc); |
|||
System.out.println(decrypt(enc, key)); |
|||
} |
|||
<syntaxhighlight lang="java">public class Vig{ |
|||
static String encrypt(String text, final String key) { |
|||
static String encodedMessage = |
|||
"MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA FWAML ZZRXJ EKAHV FASMU LVVUT TGK"; |
|||
text = text.toUpperCase(); |
|||
for (int i = 0, j = 0; i < text.length(); i++) { |
|||
final static double freq[] = { |
|||
char c = text.charAt(i); |
|||
0.08167, 0.01492, 0.02782, 0.04253, 0.12702, 0.02228, 0.02015, |
|||
if (c < 'A' || c > 'Z') continue; |
|||
0.06094, 0.06966, 0.00153, 0.00772, 0.04025, 0.02406, 0.06749, |
|||
res += (char)((c + key.charAt(j) - 2 * 'A') % 26 + 'A'); |
|||
0.07507, 0.01929, 0.00095, 0.05987, 0.06327, 0.09056, 0.02758, |
|||
j = ++j % key.length(); |
|||
0.00978, 0.02360, 0.00150, 0.01974, 0.00074 |
|||
}; |
|||
public static void main(String[] args) { |
|||
int lenghtOfEncodedMessage = encodedMessage.length(); |
|||
char[] encoded = new char [lenghtOfEncodedMessage] ; |
|||
char[] key = new char [lenghtOfEncodedMessage] ; |
|||
encodedMessage.getChars(0, lenghtOfEncodedMessage, encoded, 0); |
|||
int txt[] = new int[lenghtOfEncodedMessage]; |
|||
int len = 0, j; |
|||
double fit, best_fit = 1e100; |
|||
for (j = 0; j < lenghtOfEncodedMessage; j++) |
|||
if (Character.isUpperCase(encoded[j])) |
|||
txt[len++] = encoded[j] - 'A'; |
|||
for (j = 1; j < 30; j++) { |
|||
fit = freq_every_nth(txt, len, j, key); |
|||
System.out.printf("%f, key length: %2d ", fit, j); |
|||
System.out.print(key); |
|||
if (fit < best_fit) { |
|||
best_fit = fit; |
|||
System.out.print(" <--- best so far"); |
|||
} |
} |
||
System.out.print("\n"); |
|||
} |
} |
||
} |
|||
static String decrypt(String text, final String key) { |
static String decrypt(String text, final String key) { |
||
Line 1,309: | Line 1,131: | ||
return res; |
return res; |
||
} |
} |
||
}</lang> |
|||
static int best_match(final double []a, final double []b) { |
|||
<pre>WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY |
|||
double sum = 0, fit, d, best_fit = 1e100; |
|||
BEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCH</pre> |
|||
int i, rotate, best_rotate = 0; |
|||
for (i = 0; i < 26; i++) |
|||
=={{header|JavaScript}}== |
|||
sum += a[i]; |
|||
<lang javascript>// helpers |
|||
for (rotate = 0; rotate < 26; rotate++) { |
|||
// helper |
|||
fit = 0; |
|||
function ordA(a) { |
|||
for (i = 0; i < 26; i++) { |
|||
return a.charCodeAt(0) - 65; |
|||
d = a[(i + rotate) % 26] / sum - b[i]; |
|||
fit += d * d / b[i]; |
|||
} |
|||
if (fit < best_fit) { |
|||
best_fit = fit; |
|||
best_rotate = rotate; |
|||
} |
|||
} |
|||
return best_rotate; |
|||
} |
} |
||
static double freq_every_nth(final int []msg, int len, int interval, char[] key) { |
|||
// vigenere |
|||
double sum, d, ret; |
|||
function vigenere(text, key, decode) { |
|||
double [] accu = new double [26]; |
|||
var i = 0, b; |
|||
double [] out = new double [26]; |
|||
key = key.toUpperCase().replace(/[^A-Z]/g, ''); |
|||
int i, j, rot; |
|||
return text.toUpperCase().replace(/[^A-Z]/g, '').replace(/[A-Z]/g, function(a) { |
|||
b = key[i++ % key.length]; |
|||
for (j = 0; j < interval; j++) { |
|||
return String.fromCharCode(((ordA(a) + (decode ? 26 - ordA(b) : ordA(b))) % 26 + 65)); |
|||
for (i = 0; i < 26; i++) |
|||
}); |
|||
out[i] = 0; |
|||
} |
|||
for (i = j; i < len; i += interval) |
|||
out[msg[i]]++; |
|||
// example |
|||
rot = best_match(out, freq); |
|||
var text = "The quick brown fox Jumped over the lazy Dog the lazy dog lazy dog dog"; |
|||
try{ |
|||
var key = 'alex'; |
|||
key[j] = (char)(rot + 'A'); |
|||
var enc = vigenere(text,key); |
|||
} catch (Exception e) { |
|||
var dec = vigenere(enc,key,true); |
|||
System.out.print(e.getMessage()); |
|||
} |
|||
console.log(enc); |
|||
for (i = 0; i < 26; i++) |
|||
console.log(dec);</lang> |
|||
accu[i] += out[(i + rot) % 26]; |
|||
} |
|||
=={{header|Jsish}}== |
|||
From Javascript entry. |
|||
for (i = 0, sum = 0; i < 26; i++) |
|||
<lang javascript>/* Vigenère cipher, in Jsish */ |
|||
sum += accu[i]; |
|||
"use strict"; |
|||
for (i = 0, ret = 0; i < 26; i++) { |
|||
function ordA(a:string):number { |
|||
d = accu[i] / sum - freq[i]; |
|||
ret += d * d / freq[i]; |
|||
} |
|||
key[interval] = '\0'; |
|||
return ret; |
|||
} |
} |
||
// vigenere |
|||
function vigenereCipher(text:string, key:string, decode:boolean=false):string { |
|||
var i = 0, b; |
|||
key = key.toUpperCase().replace(/[^A-Z]/g, ''); |
|||
return text.toUpperCase().replace(/[^A-Z]/g, '').replace(/[A-Z]/g, |
|||
function(a:string, idx:number, str:string) { |
|||
b = key[i++ % key.length]; |
|||
return String.fromCharCode(((ordA(a) + (decode ? 26 - ordA(b) : ordA(b))) % 26 + 65)); |
|||
}); |
|||
} |
} |
||
</syntaxhighlight> |
|||
provide('vigenereCipher', 1); |
|||
if (Interp.conf('unitTest')) { |
|||
var text = "The quick brown fox Jumped over the lazy Dog the lazy dog lazy dog dog"; |
|||
var key = 'jsish'; |
|||
var enc = vigenereCipher(text, key); |
|||
; text; |
|||
; enc; |
|||
; vigenereCipher(enc, key, true); |
|||
} |
|||
/* |
|||
=!EXPECTSTART!= |
|||
text ==> The quick brown fox Jumped over the lazy Dog the lazy dog lazy dog dog |
|||
enc ==> CZMIBRUSTYXOVXVGBCEWNVWNLALPWSJRGVVPLPWSJRGVVPDIRFMGOVVP |
|||
vigenere(enc, key, true) ==> THEQUICKBROWNFOXJUMPEDOVERTHELAZYDOGTHELAZYDOGLAZYDOGDOG |
|||
=!EXPECTEND!= |
|||
*/</lang> |
|||
{{out}} |
|||
<pre>prompt$ jsish -u vigenereCipher.jsi |
|||
[PASS] vigenereCipher.jsi</pre> |
|||
=={{header|Julia}}== |
=={{header|Julia}}== |
||
{{works with|Julia|0.6}} |
|||
<lang Julia>function encrypt(msg::AbstractString, key::AbstractString) |
|||
msg = uppercase(join(filter(isalpha, collect(msg)))) |
|||
key = uppercase(join(filter(isalpha, collect(key)))) |
|||
msglen = length(msg) |
|||
keylen = length(key) |
|||
<syntaxhighlight lang="julia"># ciphertext block {{{1 |
|||
if keylen < msglen |
|||
const ciphertext = filter(isalpha, """ |
|||
key = repeat(key, div(msglen - keylen, keylen) + 2)[1:msglen] |
|||
MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH |
|||
end |
|||
VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD |
|||
ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS |
|||
FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG |
|||
ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ |
|||
ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS |
|||
JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT |
|||
LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST |
|||
MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH |
|||
QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV |
|||
RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW |
|||
TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO |
|||
SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR |
|||
ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX |
|||
BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB |
|||
BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA |
|||
FWAML ZZRXJ EKAHV FASMU LVVUT TGK |
|||
""") |
|||
# }}} |
|||
# character frequencies {{{1 |
|||
enc = Vector{Char}(msglen) |
|||
const letters = Dict{Char, Float32}( |
|||
'E' => 12.702, |
|||
@inbounds for i in 1:length(msg) |
|||
'T' => 9.056, |
|||
enc[i] = Char((Int(msg[i]) + Int(key[i]) - 130) % 26 + 65) |
|||
'A' => 8.167, |
|||
'O' => 7.507, |
|||
'I' => 6.966, |
|||
'N' => 6.749, |
|||
end |
|||
'S' => 6.327, |
|||
'H' => 6.094, |
|||
'R' => 5.987, |
|||
'D' => 4.253, |
|||
'L' => 4.025, |
|||
'C' => 2.782, |
|||
'U' => 2.758, |
|||
'M' => 2.406, |
|||
'W' => 2.361, |
|||
'F' => 2.228, |
|||
'G' => 2.015, |
|||
'Y' => 1.974, |
|||
'P' => 1.929, |
|||
'B' => 1.492, |
|||
'V' => 0.978, |
|||
'K' => 0.772, |
|||
'J' => 0.153, |
|||
'X' => 0.150, |
|||
'Q' => 0.095, |
|||
'Z' => 0.074) |
|||
const digraphs = Dict{AbstractString, Float32}( |
|||
"TH" => 15.2, |
|||
"HE" => 12.8, |
|||
"IN" => 9.4, |
|||
"ER" => 9.4, |
|||
"AN" => 8.2, |
|||
"RE" => 6.8, |
|||
"ND" => 6.3, |
|||
"AT" => 5.9, |
|||
"ON" => 5.7, |
|||
"NT" => 5.6, |
|||
"HA" => 5.6, |
|||
"ES" => 5.6, |
|||
"ST" => 5.5, |
|||
"EN" => 5.5, |
|||
"ED" => 5.3, |
|||
"TO" => 5.2, |
|||
"IT" => 5.0, |
|||
"OU" => 5.0, |
|||
"EA" => 4.7, |
|||
"HI" => 4.6, |
|||
"IS" => 4.6, |
|||
"OR" => 4.3, |
|||
"TI" => 3.4, |
|||
"AS" => 3.3, |
|||
"TE" => 2.7, |
|||
"ET" => 1.9, |
|||
"NG" => 1.8, |
|||
"OF" => 1.6, |
|||
"AL" => 0.9, |
|||
"DE" => 0.9, |
|||
"SE" => 0.8, |
|||
"LE" => 0.8, |
|||
"SA" => 0.6, |
|||
"SI" => 0.5, |
|||
"AR" => 0.4, |
|||
"VE" => 0.4, |
|||
"RA" => 0.4, |
|||
"LD" => 0.2, |
|||
"UR" => 0.2) |
|||
const trigraphs = Dict{AbstractString, Float32}( |
|||
"THE" => 18.1, |
|||
"AND" => 7.3, |
|||
"ING" => 7.2, |
|||
"ION" => 4.2, |
|||
"ENT" => 4.2, |
|||
"HER" => 3.6, |
|||
"FOR" => 3.4, |
|||
"THA" => 3.3, |
|||
"NTH" => 3.3, |
|||
"INT" => 3.2, |
|||
"TIO" => 3.1, |
|||
"ERE" => 3.1, |
|||
"TER" => 3.0, |
|||
"EST" => 2.8, |
|||
"ERS" => 2.8, |
|||
"HAT" => 2.6, |
|||
"ATI" => 2.6, |
|||
"ATE" => 2.5, |
|||
"ALL" => 2.5, |
|||
"VER" => 2.4, |
|||
"HIS" => 2.4, |
|||
"HES" => 2.4, |
|||
"ETH" => 2.4, |
|||
"OFT" => 2.2, |
|||
"STH" => 2.1, |
|||
"RES" => 2.1, |
|||
"OTH" => 2.1, |
|||
"ITH" => 2.1, |
|||
"FTH" => 2.1, |
|||
"ONT" => 2.0) |
|||
# 1}}} |
|||
function decrypt(enc:: |
function decrypt(enc::ASCIIString, key::ASCIIString) |
||
const enclen = length(enc) |
|||
enc = uppercase(join(filter(isalpha, collect(enc)))) |
|||
const keylen = length(key) |
|||
key = uppercase(join(filter(isalpha, collect(key)))) |
|||
msglen = length(enc) |
|||
keylen = length(key) |
|||
if keylen < |
if keylen < enclen |
||
key = |
key = (key^(div(enclen - keylen, keylen) + 2))[1:enclen] |
||
end |
end |
||
msg = |
msg = Array(Char, enclen) |
||
for i=1:enclen |
|||
msg[i] = Char((Int(enc[i]) - Int(key[i]) + 26) % 26 + 65) |
msg[i] = Char((Int(enc[i]) - Int(key[i]) + 26) % 26 + 65) |
||
end |
end |
||
msg::Array{Char, 1} |
|||
return join(msg) |
|||
end |
end |
||
function cryptanalyze(enc::ASCIIString; maxkeylen::Integer = 20) |
|||
const messages = ("Attack at dawn.", "Don't attack.", "The war is over.") |
|||
const |
const enclen = length(enc) |
||
maxkey = "" |
|||
maxdec = "" |
|||
maxscore = 0.0 |
|||
for keylen=1:maxkeylen |
|||
for msg in messages |
|||
key = Array(Char, keylen) |
|||
idx = filter(x -> x % keylen == 0, 1:enclen) - keylen + 1 |
|||
dec = decrypt(enc, key) |
|||
println("Original: $msg\n -> encrypted: $enc\n -> decrypted: $dec") |
|||
end</lang> |
|||
for i=1:keylen |
|||
{{out}} |
|||
maxsubscore = 0.0 |
|||
for j='A':'Z' |
|||
<pre>Original: Attack at dawn. |
|||
subscore = 0.0 |
|||
-> encrypted: LXFOPVEFRNHR |
|||
-> decrypted: ATTACKATDAWN |
|||
Original: Don't attack. |
|||
-> encrypted: OSZHNEXMQX |
|||
-> decrypted: DONTATTACK |
|||
Original: The war is over. |
|||
-> encrypted: ELQKNCMECIPV |
|||
-> decrypted: THEWARISOVER</pre> |
|||
for k in decrypt(enc[idx], ascii(string(j))) |
|||
=={{header|Kotlin}}== |
|||
subscore += get(letters, k, 0.0) |
|||
<lang scala>// version 1.1.3 |
|||
end |
|||
if subscore > maxsubscore |
|||
fun vigenere(text: String, key: String, encrypt: Boolean = true): String { |
|||
maxsubscore = subscore |
|||
val t = if (encrypt) text.toUpperCase() else text |
|||
key[i] = j |
|||
val sb = StringBuilder() |
|||
end |
|||
end |
|||
if (c !in 'A'..'Z') continue |
|||
val ci = if (encrypt) |
|||
(c.toInt() + key[ki].toInt() - 130) % 26 |
|||
else |
|||
(c.toInt() - key[ki].toInt() + 26) % 26 |
|||
sb.append((ci + 65).toChar()) |
|||
ki = (ki + 1) % key.length |
|||
} |
|||
return sb.toString() |
|||
} |
|||
idx += 1 |
|||
fun main(args: Array<String>) { |
|||
end |
|||
val text = "Beware the Jabberwock, my son! The jaws that bite, the claws that catch!" |
|||
val encoded = vigenere(text, key) |
|||
println(encoded) |
|||
val decoded = vigenere(encoded, key, false) |
|||
println(decoded) |
|||
}</lang> |
|||
key = join(key) |
|||
{{out}} |
|||
const dec = decrypt(enc, key) |
|||
<pre> |
|||
score = 0.0 |
|||
WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY |
|||
BEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCH |
|||
</pre> |
|||
for i in dec |
|||
=={{header|Liberty BASIC}}== |
|||
score += get(letters, i, 0.0) |
|||
<lang lb> |
|||
end |
|||
ori$ = "Beware the Jabberwock, my son! The jaws that bite, the claws that catch!" |
|||
key$ = filter$("vigenerecipher") |
|||
print ori$ |
|||
print key$ |
|||
enc$ = encrypt$(ori$, key$) |
|||
print enc$ |
|||
dec$ = decrypt$(enc$, key$) |
|||
print dec$ |
|||
for i=1:enclen - 2 |
|||
end |
|||
const digraph = string(dec[i], dec[i + 1]) |
|||
const trigraph = string(dec[i], dec[i + 1], dec[i + 2]) |
|||
if haskey(digraphs, digraph) |
|||
function encrypt$(text$, key$) |
|||
score += 2 * get(digraphs, digraph, 0.0) |
|||
flt$ = filter$(text$) |
|||
end |
|||
j = 1 |
|||
for i = 1 to len(flt$) |
|||
m$ = mid$(flt$, i, 1) |
|||
m = asc(m$)-asc("A") |
|||
k$ = mid$(key$, j, 1) |
|||
k = asc(k$)-asc("A") |
|||
j = (j mod len(key$)) + 1 |
|||
c = (m + k) mod 26 |
|||
c$=chr$(asc("A")+c) |
|||
encrypt$=encrypt$+c$ |
|||
next |
|||
end function |
|||
if haskey(trigraphs, trigraph) |
|||
function decrypt$(flt$, key$) |
|||
score += 3 * get(trigraphs, trigraph, 0.0) |
|||
decrypt$ = "" |
|||
end |
|||
end |
|||
m$ = mid$(flt$, i, 1) |
|||
m = asc(m$)-asc("A") |
|||
k$ = mid$(key$, j, 1) |
|||
k = asc(k$)-asc("A") |
|||
j = (j mod len(key$)) + 1 |
|||
c = (m - k + 26) mod 26 |
|||
c$=chr$(asc("A")+c) |
|||
decrypt$=decrypt$+c$ |
|||
next |
|||
end function |
|||
function filter$(ori$) |
|||
'a..z A..Z go caps, other skipped |
|||
filter$="" |
|||
for i = 1 to len(ori$) |
|||
c$ = upper$(mid$(ori$,i,1)) |
|||
if instr("ABCDEFGHIJKLMNOPQRSTUVWXYZ", c$) then filter$ = filter$ + c$ |
|||
next |
|||
end function |
|||
</lang> |
|||
Beware the Jabberwock, my son! The jaws that bite, the claws that catch! |
|||
VIGENERECIPHER |
|||
WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY |
|||
BEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCH |
|||
=={{header|Lua}}== |
|||
<lang lua>function Encrypt( _msg, _key ) |
|||
local msg = { _msg:upper():byte( 1, -1 ) } |
|||
local key = { _key:upper():byte( 1, -1 ) } |
|||
local enc = {} |
|||
if score > maxscore |
|||
maxscore = score |
|||
maxkey = key |
|||
if msg[i] >= string.byte('A') and msg[i] <= string.byte('Z') then |
|||
maxdec = dec |
|||
enc[k] = ( msg[i] + key[j] - 2*string.byte('A') ) % 26 + string.byte('A') |
|||
k = k + 1 |
|||
if j == #key then j = 1 else j = j + 1 end |
|||
end |
end |
||
end |
end |
||
(maxkey, join(maxdec))::Tuple{ASCIIString, ASCIIString} |
|||
return string.char( unpack(enc) ) |
|||
end |
end |
||
key, dec = cryptanalyze(ciphertext) |
|||
function Decrypt( _msg, _key ) |
|||
println("key: ", key, "\n\n", dec) |
|||
local msg = { _msg:byte( 1, -1 ) } |
|||
local key = { _key:upper():byte( 1, -1 ) } |
|||
local dec = {} |
|||
# post-compilation profiling run |
|||
local j = 1 |
|||
gc() |
|||
for i = 1, #msg do |
|||
t = @elapsed cryptanalyze(ciphertext) |
|||
dec[i] = ( msg[i] - key[j] + 26 ) % 26 + string.byte('A') |
|||
println("\nelapsed time: ", t, " seconds")</syntaxhighlight> |
|||
if j == #key then j = 1 else j = j + 1 end |
|||
end |
|||
return string.char( unpack(dec) ) |
|||
end |
|||
{{out}} |
|||
<pre>key: THECHESHIRECAT |
|||
original = "Beware the Jabberwock, my son! The jaws that bite, the claws that catch!" |
|||
key = "VIGENERECIPHER"; |
|||
THISWASTHEPOEMTHATALICEREADJABBERWOCKYTWASBRILLIGANDTHESLITHY... |
|||
encrypted = Encrypt( original, key ) |
|||
decrypted = Decrypt( encrypted, key ) |
|||
elapsed time: 0.042894211 seconds</pre> |
|||
print( encrypted ) |
|||
print( decrypted )</lang> |
|||
<pre>WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY |
|||
BEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCH</pre> |
|||
=={{header| |
=={{header|Kotlin}}== |
||
{{trans|C}} |
|||
<lang Mathematica>encode[text_String, key_String] := |
|||
This is a reasonably faithful translation of the C entry though I've restricted the key lengths examined to 26 to automatically produce the correct key and hence decrypted text. This is because the C entry examines key lengths up to 29 and a value of 28 gives a slightly better fit even though the key produced (THECHESCIRECATTHECHESHIRECAT) and resulting text don't make as much sense and so would be rejected if one were examining the candidate keys manually. |
|||
Module[{textCode, keyCode}, |
|||
<syntaxhighlight lang="scala">// version 1.1.3 |
|||
textCode = |
|||
Cases[ToCharacterCode[ |
|||
ToUpperCase@ |
|||
text], _?(IntervalMemberQ[Interval@{65, 90}, #] &)] - 65; |
|||
keyCode = |
|||
Cases[ToCharacterCode[ |
|||
ToUpperCase@ |
|||
key], _?(IntervalMemberQ[Interval@{65, 90}, #] &)] - 65; |
|||
keyCode = |
|||
If[Length[textCode] < Length[keyCode], |
|||
keyCode[[;; Length@textCode]], |
|||
PadRight[keyCode, Length@textCode, keyCode]]; |
|||
FromCharacterCode[Mod[textCode + keyCode, 26] + 65]] |
|||
val encoded = |
|||
decode[text_String, key_String] := |
|||
"MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH" + |
|||
Module[{textCode, keyCode}, |
|||
"VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD" + |
|||
textCode = |
|||
"ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS" + |
|||
Cases[ToCharacterCode[ |
|||
"FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG" + |
|||
ToUpperCase@ |
|||
"ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ" + |
|||
text], _?(IntervalMemberQ[Interval@{65, 90}, #] &)] - 65; |
|||
"ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS" + |
|||
keyCode = |
|||
"JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT" + |
|||
Cases[ToCharacterCode[ |
|||
"LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST" + |
|||
ToUpperCase@ |
|||
"MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH" + |
|||
key], _?(IntervalMemberQ[Interval@{65, 90}, #] &)] - 65; |
|||
"QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV" + |
|||
keyCode = |
|||
"RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW" + |
|||
If[Length[textCode] < Length[keyCode], |
|||
"TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO" + |
|||
keyCode[[;; Length@textCode]], |
|||
"SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR" + |
|||
PadRight[keyCode, Length@textCode, keyCode]]; |
|||
"ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX" + |
|||
FromCharacterCode[Mod[textCode - keyCode, 26] + 65]]</lang> |
|||
"BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB" + |
|||
"BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA" + |
|||
"FWAML ZZRXJ EKAHV FASMU LVVUT TGK" |
|||
val freq = doubleArrayOf( |
|||
<pre>key = "Vigenere Cipher"; |
|||
0.08167, 0.01492, 0.02782, 0.04253, 0.12702, 0.02228, 0.02015, |
|||
text = "Beware the Jabberwock, my son! The jaws that bite, the claws \ |
|||
0.06094, 0.06966, 0.00153, 0.00772, 0.04025, 0.02406, 0.06749, |
|||
that catch!"; |
|||
0.07507, 0.01929, 0.00095, 0.05987, 0.06327, 0.09056, 0.02758, |
|||
code = encode[text, key] |
|||
0.00978, 0.02360, 0.00150, 0.01974, 0.00074 |
|||
) |
|||
fun bestMatch(a: DoubleArray): Int { |
|||
WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY |
|||
val sum = a.sum() |
|||
var bestFit = 1e100 |
|||
var bestRotate = 0 |
|||
for (rotate in 0..25) { |
|||
var fit = 0.0 |
|||
for (i in 0..25) { |
|||
val d = a[(i + rotate) % 26] / sum - freq[i] |
|||
fit += d * d / freq[i] |
|||
} |
|||
if (fit < bestFit) { |
|||
bestFit = fit |
|||
bestRotate = rotate |
|||
} |
|||
} |
|||
return bestRotate |
|||
} |
|||
fun freqEveryNth(msg: IntArray, key: CharArray): Double { |
|||
decode[code, key] |
|||
val len = msg.size |
|||
val interval = key.size |
|||
val out = DoubleArray(26) |
|||
val accu = DoubleArray(26) |
|||
for (j in 0 until interval) { |
|||
out.fill(0.0) |
|||
for (i in j until len step interval) out[msg[i]]++ |
|||
val rot = bestMatch(out) |
|||
key[j] = (rot + 65).toChar() |
|||
for (i in 0..25) accu[i] += out[(i + rot) % 26] |
|||
} |
|||
val sum = accu.sum() |
|||
var ret = 0.0 |
|||
for (i in 0..25) { |
|||
val d = accu[i] / sum - freq[i] |
|||
ret += d * d / freq[i] |
|||
} |
|||
return ret |
|||
} |
|||
fun decrypt(text: String, key: String): String { |
|||
BEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCH |
|||
val sb = StringBuilder() |
|||
var ki = 0 |
|||
for (c in text) { |
|||
if (c !in 'A'..'Z') continue |
|||
val ci = (c.toInt() - key[ki].toInt() + 26) % 26 |
|||
sb.append((ci + 65).toChar()) |
|||
ki = (ki + 1) % key.length |
|||
} |
|||
return sb.toString() |
|||
} |
|||
fun main(args: Array<String>) { |
|||
</pre> |
|||
val enc = encoded.replace(" ", "") |
|||
val txt = IntArray(enc.length) { enc[it] - 'A' } |
|||
var bestFit = 1e100 |
|||
var bestKey = "" |
|||
val f = "%f %2d %s" |
|||
println(" Fit Length Key") |
|||
for (j in 1..26) { |
|||
val key = CharArray(j) |
|||
val fit = freqEveryNth(txt, key) |
|||
val sKey = key.joinToString("") |
|||
print(f.format(fit, j, sKey)) |
|||
if (fit < bestFit) { |
|||
bestFit = fit |
|||
bestKey = sKey |
|||
print(" <--- best so far") |
|||
} |
|||
println() |
|||
} |
|||
println() |
|||
println("Best key : $bestKey") |
|||
println("\nDecrypted text:\n${decrypt(enc, bestKey)}") |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
=={{header|NetRexx}}== |
|||
<pre> |
|||
<lang NetRexx>/* NetRexx */ |
|||
Fit Length Key |
|||
options replace format comments java crossref savelog symbols nobinary |
|||
2.984348 1 E <--- best so far |
|||
2.483684 2 EC <--- best so far |
|||
2.642487 3 TEE |
|||
1.976651 4 THEC <--- best so far |
|||
2.356881 5 EEEPU |
|||
2.203129 6 TCECEC |
|||
1.051163 7 THECSAS <--- best so far |
|||
1.645763 8 TJQGAHET |
|||
2.001380 9 VEIZSEGNT |
|||
1.824476 10 ECEGAWQTDS |
|||
1.623083 11 TNLUSRXPTAJ |
|||
1.253527 12 XLECTHQGTHEC |
|||
1.399037 13 LJJTDGFNOTENR |
|||
0.152370 14 THECHESHIRECAT <--- best so far |
|||
1.533951 15 JNTOOEEXFTGQTNH |
|||
1.068182 16 TJTSAEETEXHPXHNE |
|||
1.034093 17 AZRAXUHEJLREEXIEE |
|||
1.443345 18 VNIZQPALEPTSXSEXUC |
|||
1.090977 19 FUCAITCSLVTEZDUDEHS |
|||
0.979868 20 EQXGAHWTTQECEWUGXHPI |
|||
0.789410 21 HVRCSAFTHEBDLSTAERSES |
|||
0.881380 22 TVIJTCIGKAQPELECRXPTNC |
|||
0.952456 23 KKEQXGPWTCQEELIEHXUWASV |
|||
0.715968 24 ELAIXHQTTIEDXJETTNTGAEPC |
|||
0.891258 25 OTJUUEGERDNQTUQEAGWUTIEOA |
|||
0.852784 26 IGITEGECAGAVUNLJAHASAVTETW |
|||
Best key : THECHESHIRECAT |
|||
pt = 'Attack at dawn!' |
|||
key = 'LEMON' |
|||
test(key, pt) |
|||
Decrypted text: |
|||
key = 'N' -- rot-13 |
|||
THISWASTHEPOEMTHATALICEREADJABBERWOCKYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIMBLEINTHEWABEALLMIMSYWERETHEBOROGOVESANDTHEMOMERATHSOUTGRABEBEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCHBEWARETHEJUBJUBBIRDANDSHUNTHEFRUMIOUSBANDERSNATCHHETOOKHISVORPALSWORDINHANDLONGTIMETHEMANXOMEFOEHESOUGHTSORESTEDHEBYTHETUMTUMTREEANDSTOODAWHILEINTHOUGHTANDASINUFFISHTHOUGHTHESTOODTHEJABBERWOCKWITHEYESOFFLAMECAMEWHIFFLINGTHROUGHTHETULGEYWOODANDBURBLEDASITCAMEONETWOONETWOANDTHROUGHANDTHROUGHTHEVORPALBLADEWENTSNICKERSNACKHELEFTITDEADANDWITHITSHEADHEWENTGALUMPHINGBACKANDHASTTHOUSLAINTHEJABBERWOCKCOMETOMYARMSMYBEAMISHBOYOFRABJOUSDAYCALLOOHCALLAYHECHORTLEDINHISJOYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIMBLEINTHEWABEALLMIMSYWERETHEBOROGOVESANDTHEMOMERATHSOUTGRABEITSEEMSVERYPRETTYSHESAIDWHENSHEHADFINISHEDITBUTITSRATHERHARDTOUNDERSTAND |
|||
test(key, pt) |
|||
</pre> |
|||
=={{header|Nim}}== |
|||
key = 'B' -- Caesar |
|||
{{trans|Julia}} |
|||
test(key, pt) |
|||
{{trans|Phix}} |
|||
This is a translation of Julia algorithm with some ideas from Phix translation. |
|||
<syntaxhighlight lang="nim">import sequtils, strutils, sugar, tables, times |
|||
const |
|||
pt = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' |
|||
key = 'A' |
|||
test(key, pt) |
|||
CipherText = """MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH |
|||
pt = sampledata() |
|||
VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD |
|||
key = 'Hamlet; Prince of Denmark' |
|||
ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS |
|||
test(key, pt) |
|||
FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG |
|||
ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ |
|||
ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS |
|||
JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT |
|||
LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST |
|||
MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH |
|||
QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV |
|||
RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW |
|||
TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO |
|||
SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR |
|||
ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX |
|||
BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB |
|||
BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA |
|||
FWAML ZZRXJ EKAHV FASMU LVVUT TGK""".splitWhitespace.join() |
|||
FreqLetters = {'E': 12.702, 'T': 9.056, 'A': 8.167, 'O': 7.507, |
|||
return |
|||
'I': 6.966, 'N': 6.749, 'S': 6.327, 'H': 6.094, |
|||
'R': 5.987, 'D': 4.253, 'L': 4.025, 'C': 2.782, |
|||
'U': 2.758, 'M': 2.406, 'W': 2.361, 'F': 2.228, |
|||
'G': 2.015, 'Y': 1.974, 'P': 1.929, 'B': 1.492, |
|||
'V': 0.978, 'K': 0.772, 'J': 0.153, 'X': 0.150, |
|||
'Q': 0.095, 'Z': 0.074}.toTable |
|||
FreqDigraphs = {"TH": 15.2, "HE": 12.8, "IN": 9.4, "ER": 9.4, |
|||
method vigenere(meth, key, text) public static |
|||
"AN": 8.2, "RE": 6.8, "ND": 6.3, "AT": 5.9, |
|||
"ON": 5.7, "NT": 5.6, "HA": 5.6, "ES": 5.6, |
|||
"ST": 5.5, "EN": 5.5, "ED": 5.3, "TO": 5.2, |
|||
"IT": 5.0, "OU": 5.0, "EA": 4.7, "HI": 4.6, |
|||
"IS": 4.6, "OR": 4.3, "TI": 3.4, "AS": 3.3, |
|||
"TE": 2.7, "ET": 1.9, "NG": 1.8, "OF": 1.6, |
|||
"AL": 0.9, "DE": 0.9, "SE": 0.8, "LE": 0.8, |
|||
"SA": 0.6, "SI": 0.5, "AR": 0.4, "VE": 0.4, |
|||
"RA": 0.4, "LD": 0.2, "UR": 0.2}.toTable |
|||
FreqTrigraphs = {"THE": 18.1, "AND": 7.3, "ING": 7.2, "ION": 4.2, |
|||
select |
|||
"ENT": 4.2, "HER": 3.6, "FOR": 3.4, "THA": 3.3, |
|||
when 'encipher'.abbrev(meth.lower, 1) then df = 1 |
|||
"NTH": 3.3, "INT": 3.2, "TIO": 3.1, "ERE": 3.1, |
|||
when 'decipher'.abbrev(meth.lower, 1) then df = -1 |
|||
"TER": 3.0, "EST": 2.8, "ERS": 2.8, "HAT": 2.6, |
|||
otherwise signal IllegalArgumentException(meth 'must be "encipher" or "decipher"') |
|||
"ATI": 2.6, "ATE": 2.5, "ALL": 2.5, "VER": 2.4, |
|||
end |
|||
"HIS": 2.4, "HES": 2.4, "ETH": 2.4, "OFT": 2.2, |
|||
"STH": 2.1, "RES": 2.1, "OTH": 2.1, "ITH": 2.1, |
|||
"FTH": 2.1, "ONT": 2.0}.toTable |
|||
func decrypt(enc, key: string): string = |
|||
alpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' |
|||
let encLen = enc.len |
|||
let keyLen = key.len |
|||
result.setLen(encLen) |
|||
var k = 0 |
|||
for i in 0..<encLen: |
|||
result[i] = chr((ord(enc[i]) - ord(key[k]) + 26) mod 26 + ord('A')) |
|||
k = (k + 1) mod keyLen |
|||
func cryptanalyze(enc: string; maxKeyLen = 20): tuple[maxKey, maxDec: string] = |
|||
text = stringscrubber(text) |
|||
let encLen = enc.len |
|||
key = stringscrubber(key) |
|||
var maxScore = 0.0 |
|||
loop l_ = 1 to text.length() |
|||
M = alpha.pos(text.substr(l_, 1)) - 1 |
|||
k_ = (l_ - 1) // key.length() |
|||
K = alpha.pos(key.substr(k_ + 1, 1)) - 1 |
|||
C = mod((M + K * df), alpha.length()) |
|||
C = alpha.substr(C + 1, 1) |
|||
code = code || C |
|||
end l_ |
|||
for keyLen in 1..maxKeyLen: |
|||
return code |
|||
var key = newString(keyLen) |
|||
var idx = collect(newSeq): |
|||
for i in 1..encLen: |
|||
if i mod keyLen == 0: |
|||
i - keyLen |
|||
for i in 0..<keyLen: |
|||
method vigenere_encipher(key, plaintext) public static |
|||
var maxSubscore = 0.0 |
|||
for j in 'A'..'Z': |
|||
var subscore = 0.0 |
|||
let encidx = idx.mapIt(enc[it]).join() |
|||
for k in decrypt(encidx, $j): |
|||
subscore += FreqLetters[k] |
|||
if subscore > maxSubscore: |
|||
maxSubscore = subscore |
|||
key[i] = j |
|||
for item in idx.mitems: inc item |
|||
let dec = decrypt(enc, key) |
|||
return vigenere('encipher', key, plaintext) |
|||
var score = 0.0 |
|||
for i in dec: |
|||
score += FreqLetters[i] |
|||
for i in 0..(encLen - 3): |
|||
method vigenere_decipher(key, ciphertext) public static |
|||
let digraph = dec[i..(i+1)] |
|||
let trigraph = dec[i..(i+2)] |
|||
score += 2 * FreqDigraphs.getOrDefault(digraph) |
|||
score += 3 * FreqTrigraphs.getOrDefault(trigraph) |
|||
if score > maxScore: |
|||
return vigenere('decipher', key, ciphertext) |
|||
maxScore = score |
|||
result.maxKey = key |
|||
result.maxDec = dec |
|||
let t0 = cpuTime() |
|||
method mod(N = int, D = int) private static |
|||
let (key, dec) = CipherText.cryptanalyze() |
|||
echo "key: ", key, '\n' |
|||
return (D + (N // D)) // D |
|||
echo dec, '\n' |
|||
echo "Elapsed time: ", (cpuTime() - t0).formatFloat(ffDecimal, precision = 3), " s"</syntaxhighlight> |
|||
method stringscrubber(cleanup) private static |
|||
alpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' |
|||
cleanup = cleanup.upper.space(0) |
|||
loop label f_ forever |
|||
x_ = cleanup.verify(alpha) |
|||
if x_ = 0 then leave f_ |
|||
cleanup = cleanup.changestr(cleanup.substr(x_, 1), '') |
|||
end f_ |
|||
return cleanup |
|||
method test(key, pt) private static |
|||
ct = vigenere_encipher(key, pt) |
|||
display(ct) |
|||
dt = vigenere_decipher(key, ct) |
|||
display(dt) |
|||
return |
|||
method display(text) public static |
|||
line = '' |
|||
o_ = 0 |
|||
loop c_ = 1 to text.length() |
|||
b_ = o_ // 5 |
|||
o_ = o_ + 1 |
|||
if b_ = 0 then line = line' ' |
|||
line = line || text.substr(c_, 1) |
|||
end c_ |
|||
say '....+....|'.copies(8) |
|||
loop label l_ forever |
|||
parse line w1 w2 w3 w4 w5 w6 W7 w8 w9 w10 w11 w12 line |
|||
pline = w1 w2 w3 w4 w5 w6 w7 w8 w9 w10 w11 w12 |
|||
say pline.strip() |
|||
if line.strip().length() = 0 then leave l_ |
|||
end l_ |
|||
say |
|||
return |
|||
method sampledata() private static returns Rexx |
|||
NL = char('\n') |
|||
antic_disposition = Rexx[] |
|||
antic_disposition = [ - |
|||
Rexx("To be, or not to be--that is the question:" ), - |
|||
Rexx("Whether 'tis nobler in the mind to suffer" ), - |
|||
Rexx("The slings and arrows of outrageous fortune" ), - |
|||
Rexx("Or to take arms against a sea of troubles" ), - |
|||
Rexx("And by opposing end them. To die, to sleep--" ), - |
|||
Rexx("No more--and by a sleep to say we end" ), - |
|||
Rexx("The heartache, and the thousand natural shocks" ), - |
|||
Rexx("That flesh is heir to. 'Tis a consummation" ), - |
|||
Rexx("Devoutly to be wished. To die, to sleep--" ), - |
|||
Rexx("To sleep--perchance to dream: ay, there's the rub,"), - |
|||
Rexx("For in that sleep of death what dreams may come" ), - |
|||
Rexx("When we have shuffled off this mortal coil," ), - |
|||
Rexx("Must give us pause. There's the respect" ), - |
|||
Rexx("That makes calamity of so long life." ), - |
|||
Rexx("For who would bear the whips and scorns of time," ), - |
|||
Rexx("Th' oppressor's wrong, the proud man's contumely" ), - |
|||
Rexx("The pangs of despised love, the law's delay," ), - |
|||
Rexx("The insolence of office, and the spurns" ), - |
|||
Rexx("That patient merit of th' unworthy takes," ), - |
|||
Rexx("When he himself might his quietus make" ), - |
|||
Rexx("With a bare bodkin? Who would fardels bear," ), - |
|||
Rexx("To grunt and sweat under a weary life," ), - |
|||
Rexx("But that the dread of something after death," ), - |
|||
Rexx("The undiscovered country, from whose bourn" ), - |
|||
Rexx("No traveller returns, puzzles the will," ), - |
|||
Rexx("And makes us rather bear those ills we have" ), - |
|||
Rexx("Than fly to others that we know not of?" ), - |
|||
Rexx("Thus conscience does make cowards of us all," ), - |
|||
Rexx("And thus the native hue of resolution" ), - |
|||
Rexx("Is sicklied o'er with the pale cast of thought," ), - |
|||
Rexx("And enterprise of great pith and moment" ), - |
|||
Rexx("With this regard their currents turn awry" ), - |
|||
Rexx("And lose the name of action. -- Soft you now," ), - |
|||
Rexx("The fair Ophelia! -- Nymph, in thy orisons" ), - |
|||
Rexx("Be all my sins remembered." ) - |
|||
] |
|||
melancholy_dane = Rexx('') |
|||
loop l_ = 0 for antic_disposition.length |
|||
melancholy_dane = melancholy_dane || antic_disposition[l_] || NL |
|||
end l_ |
|||
return melancholy_dane |
|||
</lang> |
|||
{{out}} |
{{out}} |
||
<pre>key: THECHESHIRECAT |
|||
<pre style="height: 60ex; overflow:scroll"> |
|||
....+....|....+....|....+....|....+....|....+....|....+....|....+....|....+....| |
|||
LXFOP VEFRN HR |
|||
....+....|....+....|....+....|....+....|....+....|....+....|....+....|....+....| |
|||
ATTAC KATDA WN |
|||
....+....|....+....|....+....|....+....|....+....|....+....|....+....|....+....| |
|||
NGGNP XNGQN JA |
|||
....+....|....+....|....+....|....+....|....+....|....+....|....+....|....+....| |
|||
ATTAC KATDA WN |
|||
....+....|....+....|....+....|....+....|....+....|....+....|....+....|....+....| |
|||
BUUBD LBUEB XO |
|||
....+....|....+....|....+....|....+....|....+....|....+....|....+....|....+....| |
|||
ATTAC KATDA WN |
|||
....+....|....+....|....+....|....+....|....+....|....+....|....+....|....+....| |
|||
ABCDE FGHIJ KLMNO PQRST UVWXY Z |
|||
....+....|....+....|....+....|....+....|....+....|....+....|....+....|....+....| |
|||
ABCDE FGHIJ KLMNO PQRST UVWXY Z |
|||
....+....|....+....|....+....|....+....|....+....|....+....|....+....|....+....| |
|||
AONPS KCFBG QFSYK EGUSK RLQGP WMXFV JJIHM HVGUS EYILQ CMGIY MZKRR YRWHR |
|||
FVBAH QDPBC XANPH OWUSJ EOWYB TDLKX DLASQ VHZQI BDTFD HKQLV FHRON KRGYD |
|||
WRMOW DYOGM PXHRV QDCCU SSFUN XOUDF SIFIF LVGXC XOIRB NFWVR QLRWQ PIFNI |
|||
SUWSF MYNOL NPELX WVIEV EQMHE APTYO AHAFW TCUVN VYFFO WUACB CAHME JETJP |
|||
VULSN UXBFI JKJOZ DYFBR BVQRR JYSHF LPDVB QHMLW VLGQH WJWSF XEVZA OEWIX |
|||
EGMEE LOSFI GADIO HMMJX ATIMF VLSWX FSARZ XAHME WETVX BHHSF WLJTA KNYEM |
|||
XWFPP KBOIK MHRJQ HRFLS TFJYA VLBHJ HMLWZ ARKKS CATPF JJBTK ZSZVT NGSVD |
|||
OEDPW MWVZR UTSHW XUMTD KREEN EEPDQ GASTX RPBZG CSMER ZVPWF EBWPR GHEEF |
|||
HVGOI BDEGS JKBTR GTIXV YEKRV PBCIL HFZFY VCSJX UQPIY BDYLR LRFVG WQSQB |
|||
XUQPR XNSAQ HXHGQ FGHZT YIGTE CKDSP PPTNK PRKRG TOIAO EFPVF RTGXP ELGJI |
|||
GUXVA ETYKA PMEMX CKURT MHTIX UGNNY YTTJX TZVAJ JIBMH LVYSV VMMUR LMWZA |
|||
DWMSY XWZMK VGPTT LFTGV JBFOW SZLBI OLVKF MCHXA JJRCV HTJVH ZTRXK SIPEM |
|||
JELRT EKJDV LXIWO IUFEL TIKPR FVSFG SSEOD OAHUY KTUKM EFIOY KXUQU ENPSO |
|||
ZZXGV LPQYB YUCSD ODGOO EPFHJ IVAQX FFYIY XEIBL TGCRL ELHMN IGYKI JULCK |
|||
UDYLO XHLAE CXVJU FRMRK RVSQT PEHNM UCZSY KEARL PDVOF SIKHK PNVAS PQSJZ |
|||
OKYMT TFWVD EAPKI BHHHB QSDKR EOZAT GUABH YGFOP NZDKR BSFSI GPKQI GLIJR |
|||
JEQSF VBTUZ RBHJQ PMPWJ GSRDW ZDOTT PTTAV KNUXC KWLBG GYDHN PPRMT IXEKW |
|||
STIKE QAKZP TTLRW BFURP XKNWL GTIJB LGMCH MWVQE EYFWH RGETL BUAIC CTCUT |
|||
BUIHM HRNYE FPHCF TSGHF NGASI SRAGT EWKPR AALXA ZIAAQ DMLRG TYFBP SAYWU |
|||
TRTYO CGNQW EQMVW IEDPH |
|||
THISWASTHEPOEMTHATALICEREADJABBERWOCKYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIMBLEINTHEWABEALLMIMSYWERETHEBOROGOVESANDTHEMOMERATHSOUTGRABEBEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCHBEWARETHEJUBJUBBIRDANDSHUNTHEFRUMIOUSBANDERSNATCHHETOOKHISVORPALSWORDINHANDLONGTIMETHEMANXOMEFOEHESOUGHTSORESTEDHEBYTHETUMTUMTREEANDSTOODAWHILEINTHOUGHTANDASINUFFISHTHOUGHTHESTOODTHEJABBERWOCKWITHEYESOFFLAMECAMEWHIFFLINGTHROUGHTHETULGEYWOODANDBURBLEDASITCAMEONETWOONETWOANDTHROUGHANDTHROUGHTHEVORPALBLADEWENTSNICKERSNACKHELEFTITDEADANDWITHITSHEADHEWENTGALUMPHINGBACKANDHASTTHOUSLAINTHEJABBERWOCKCOMETOMYARMSMYBEAMISHBOYOFRABJOUSDAYCALLOOHCALLAYHECHORTLEDINHISJOYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIMBLEINTHEWABEALLMIMSYWERETHEBOROGOVESANDTHEMOMERATHSOUTGRABEITSEEMSVERYPRETTYSHESAIDWHENSHEHADFINISHEDITBUTITSRATHERHARDTOUNDERSTAND |
|||
....+....|....+....|....+....|....+....|....+....|....+....|....+....|....+....| |
|||
TOBEO RNOTT OBETH ATIST HEQUE STION WHETH ERTIS NOBLE RINTH EMIND TOSUF |
|||
FERTH ESLIN GSAND ARROW SOFOU TRAGE OUSFO RTUNE ORTOT AKEAR MSAGA INSTA |
|||
SEAOF TROUB LESAN DBYOP POSIN GENDT HEMTO DIETO SLEEP NOMOR EANDB YASLE |
|||
EPTOS AYWEE NDTHE HEART ACHEA NDTHE THOUS ANDNA TURAL SHOCK STHAT FLESH |
|||
ISHEI RTOTI SACON SUMMA TIOND EVOUT LYTOB EWISH EDTOD IETOS LEEPT OSLEE |
|||
PPERC HANCE TODRE AMAYT HERES THERU BFORI NTHAT SLEEP OFDEA THWHA TDREA |
|||
MSMAY COMEW HENWE HAVES HUFFL EDOFF THISM ORTAL COILM USTGI VEUSP AUSET |
|||
HERES THERE SPECT THATM AKESC ALAMI TYOFS OLONG LIFEF ORWHO WOULD BEART |
|||
HEWHI PSAND SCORN SOFTI METHO PPRES SORSW RONGT HEPRO UDMAN SCONT UMELY |
|||
THEPA NGSOF DESPI SEDLO VETHE LAWSD ELAYT HEINS OLENC EOFOF FICEA NDTHE |
|||
SPURN STHAT PATIE NTMER ITOFT HUNWO RTHYT AKESW HENHE HIMSE LFMIG HTHIS |
|||
QUIET USMAK EWITH ABARE BODKI NWHOW OULDF ARDEL SBEAR TOGRU NTAND SWEAT |
|||
UNDER AWEAR YLIFE BUTTH ATTHE DREAD OFSOM ETHIN GAFTE RDEAT HTHEU NDISC |
|||
OVERE DCOUN TRYFR OMWHO SEBOU RNNOT RAVEL LERRE TURNS PUZZL ESTHE WILLA |
|||
NDMAK ESUSR ATHER BEART HOSEI LLSWE HAVET HANFL YTOOT HERST HATWE KNOWN |
|||
OTOFT HUSCO NSCIE NCEDO ESMAK ECOWA RDSOF USALL ANDTH USTHE NATIV EHUEO |
|||
FRESO LUTIO NISSI CKLIE DOERW ITHTH EPALE CASTO FTHOU GHTAN DENTE RPRIS |
|||
EOFGR EATPI THAND MOMEN TWITH THISR EGARD THEIR CURRE NTSTU RNAWR YANDL |
|||
OSETH ENAME OFACT IONSO FTYOU NOWTH EFAIR OPHEL IANYM PHINT HYORI SONSB |
|||
EALLM YSINS REMEM BERED |
|||
</pre> |
|||
Elapsed time: 0.041 s</pre> |
|||
=={{header|Nim}}== |
|||
<lang nim>import strutils |
|||
proc isAlpha(c): bool = c in 'a'..'z' or c in 'A'..'Z' |
|||
proc encrypt(msg, key): string = |
|||
result = "" |
|||
var pos = 0 |
|||
for c in msg: |
|||
if isAlpha c: |
|||
result.add chr(((ord(key[pos]) + ord(toUpper c)) mod 26) + ord('A')) |
|||
pos = (pos + 1) mod key.len |
|||
proc decrypt(msg, key): string = |
|||
result = "" |
|||
var pos = 0 |
|||
for c in msg: |
|||
result.add chr(((26 + ord(c) - ord(key[pos])) mod 26) + ord('A')) |
|||
pos = (pos + 1) mod key.len |
|||
const text = "Beware the Jabberwock, my son! The jaws that bite, the claws that catch!" |
|||
const key = "VIGENERECIPHER" |
|||
let encr = encrypt(text, key) |
|||
let decr = decrypt(encr, key) |
|||
echo text |
|||
echo encr |
|||
echo decr</lang> |
|||
{{out}} |
|||
<pre>Beware the Jabberwock, my son! The jaws that bite, the claws that catch! |
|||
WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY |
|||
BEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCH</pre> |
|||
=={{header|Objeck}}== |
|||
{{trans|D}} |
|||
<lang objeck> |
|||
bundle Default { |
|||
class VigenereCipher { |
|||
function : Main(args : String[]) ~ Nil { |
|||
key := "VIGENERECIPHER"; |
|||
ori := "Beware the Jabberwock, my son! The jaws that bite, the claws that catch!"; |
|||
enc := encrypt(ori, key); |
|||
IO.Console->Print("encrypt: ")->PrintLine(enc); |
|||
IO.Console->Print("decrypt: ")->PrintLine(decrypt(enc, key)); |
|||
} |
|||
function : native : encrypt(text : String, key : String) ~ String { |
|||
res := ""; |
|||
text := text->ToUpper(); |
|||
j := 0; |
|||
each(i : text) { |
|||
c := text->Get(i); |
|||
if(c >= 'A' & c <= 'Z') { |
|||
res->Append(((c + key->Get(j) - 2 * 'A') % 26 + 'A')->As(Char)); |
|||
j += 1; |
|||
j := j % key->Size(); |
|||
}; |
|||
}; |
|||
return res; |
|||
} |
|||
function : native : decrypt(text : String, key : String) ~ String { |
|||
res := ""; |
|||
text := text->ToUpper(); |
|||
j := 0; |
|||
each(i : text) { |
|||
c := text->Get(i); |
|||
if(c >= 'A' & c <= 'Z') { |
|||
res->Append(((c - key->Get(j) + 26) % 26 + 'A')->As(Char)); |
|||
j += 1; |
|||
j := j % key->Size(); |
|||
}; |
|||
}; |
|||
return res; |
|||
} |
|||
} |
|||
} |
|||
</lang> |
|||
<pre> |
|||
encrypt: WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY |
|||
decrypt: BEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCH |
|||
</pre> |
|||
=={{header|OCaml}}== |
=={{header|OCaml}}== |
||
Original version by [http://rosettacode.org/wiki/User:Vanyamil User:Vanyamil]. |
|||
{{trans|C}} |
|||
Uses the Vigenere decrypt function from the Vigenere task solution (not included in the code below). |
|||
<lang ocaml>let cipher src key crypt = |
|||
{{works with|OCaml|above 4.05}} |
|||
let str = String.uppercase src in |
|||
<syntaxhighlight lang="ocaml"> |
|||
let key = String.uppercase key in |
|||
(* Task : Vigenere cipher/Cryptanalysis *) |
|||
(* |
|||
(* strip out non-letters *) |
|||
Given some text you suspect has been encrypted |
|||
let len = String.length str in |
|||
with a Vigenère cipher, extract the key and plaintext. |
|||
let rec aux i j = |
|||
Uses correlation factors similar to other solutions. |
|||
if j >= len then String.sub str 0 i else |
|||
(originally tried Friedman test, didn't produce good result) |
|||
if str.[j] >= 'A' && str.[j] <= 'Z' |
|||
then (str.[i] <- str.[j]; aux (succ i) (succ j)) |
|||
Coded in a way that allows non-english (by passing frequencies). |
|||
else aux i (succ j) |
|||
*) |
|||
in |
|||
let res = aux 0 0 in |
|||
(*** Helpers ***) |
|||
let slen = String.length res in |
|||
let klen = String.length key in |
|||
(* Implementation of Float.round to avoid v4.08 *) |
|||
let d = int_of_char in |
|||
let round (x : float) : float = |
|||
let f = |
|||
let rem = mod_float x 1. in |
|||
if crypt |
|||
if rem >= 0.5 |
|||
then fun i -> d res.[i] - d 'A' + d key.[i mod klen] - d 'A' |
|||
then ceil x |
|||
else fun i -> d res.[i] - d key.[i mod klen] + 26 |
|||
else floor x |
|||
in |
|||
for i = 0 to pred slen do |
|||
res.[i] <- char_of_int (d 'A' + (f i) mod 26) |
|||
done; |
|||
(res) |
|||
(* A function that updates array element at a position *) |
|||
let () = |
|||
let array_update (arr : 'a array) (idx : int) (update : 'a -> 'a) : unit = |
|||
let str = "Beware the Jabberwock, my son! The jaws that bite, \ |
|||
let curr = Array.get arr idx in |
|||
Array.set arr idx (update curr) |
|||
let key = "VIGENERECIPHER" in |
|||
(*** Actual task at hand ***) |
|||
let cod = cipher str key true in |
|||
let dec = cipher cod key false in |
|||
(* the n'th element of array is how often the n'th letter was found *) |
|||
Printf.printf "Text: %s\n" str; |
|||
let observe_coincidences ?(step : int = 1) ?(offset : int = 0) (text : string) : int array = |
|||
Printf.printf "key: %s\n" key; |
|||
let arr = Array.make 26 0 in |
|||
Printf.printf "Code: %s\n" cod; |
|||
let a_code = Char.code 'A' in |
|||
Printf.printf "Back: %s\n" dec; |
|||
String.iteri (fun idx c -> if idx mod step = offset then array_update arr (Char.code c - a_code) succ) text; |
|||
;;</lang> |
|||
arr |
|||
(* Obtain correlation factor for the observed coincidences *) |
|||
Run: |
|||
let correlation_factor ?(sort : bool = true) (coincidences : int array) (freqs : float list) : float = |
|||
let clist = Array.to_list coincidences in |
|||
let clist = (if sort then List.sort compare clist else clist) in |
|||
List.fold_left2 (fun acc c f -> acc +. (float_of_int c *. f)) 0. clist freqs |
|||
(* Translation of the test used in other Rosetta Code solutions *) |
|||
<pre>$ ocaml vigenere_cipher.ml |
|||
let shifted_coincidences_test (freqs : float list) (text : string) : int = |
|||
Text: Beware the Jabberwock, my son! The jaws that bite, the claws that catch! |
|||
let sorted_freqs = List.sort compare freqs in |
|||
key: VIGENERECIPHER |
|||
let bestCorr = -100. in |
|||
Code: WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY |
|||
let max_keylen = String.length text / 20 in |
|||
Back: BEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCH</pre> |
|||
let rec helper idx (cur_len, cur_corr) (best_len, best_corr) = |
|||
if cur_len = max_keylen then (* Finished testing everything *) |
|||
best_len |
|||
else if idx = cur_len then (* Finished testing this key length *) |
|||
let (best_len, best_corr) = if cur_corr > best_corr then (cur_len, cur_corr) else (best_len, best_corr) in |
|||
helper 0 (cur_len + 1, ~-.0.5 *. float_of_int (cur_len + 1)) (best_len, best_corr) |
|||
else |
|||
let coincidences = observe_coincidences ~step:cur_len ~offset:idx text in |
|||
let factor = correlation_factor coincidences sorted_freqs in |
|||
helper (succ idx) (cur_len, cur_corr +. factor) (best_len, best_corr) |
|||
in |
|||
helper 0 (2, ~-.1.) (1, ~-.100.) |
|||
(* Returns the most likely shift value for this set *) |
|||
=={{header|ooRexx}}== |
|||
let break_caesar ?(step : int = 1) ?(offset : int = 0) (text : string) (freqs : float list) : int = |
|||
{{trans|NetRexx}} |
|||
let c_arr = observe_coincidences ~step ~offset text in |
|||
A reworking of the [[#NetRexx|NetRexx]] version using Open Object Rexx but shouldn't take much to translate to Classic Rexx. |
|||
let rec helper l curShift (maxShift, maxCorr) = |
|||
<lang REXX>/* Rexx */ |
|||
if curShift = 26 |
|||
Do |
|||
then maxShift |
|||
alpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' |
|||
else |
|||
let corr = correlation_factor ~sort:false c_arr l in |
|||
let l' = List.tl l @ [List.hd l] in |
|||
if corr > maxCorr |
|||
then helper l' (curShift + 1) (curShift, corr) |
|||
else helper l' (curShift + 1) (maxShift, maxCorr) |
|||
in |
|||
helper freqs 0 (-1, -100.) |
|||
let break (keylen : int) (text : string) (freqs : float list) : key = |
|||
pt = 'Attack at dawn!' |
|||
let rec getCaesars idx acc = |
|||
Call test key, pt |
|||
if idx >= keylen then acc else |
|||
let shift = break_caesar ~step:keylen ~offset:idx text freqs in |
|||
let new_code = if shift = 0 then Char.code 'A' else Char.code 'Z' + 1 - shift in |
|||
getCaesars (succ idx) (acc ^ Char.(new_code |> chr |> escaped)) |
|||
in |
|||
getCaesars 0 "" |
|||
let cryptanalyze (freqs : float list) (text : string) : key * string = |
|||
key = 'N' |
|||
let text = ascii_upper_letters_only text in |
|||
Call test key, pt |
|||
let keylen = shifted_coincidences_test freqs text in |
|||
let key = break keylen text freqs in |
|||
let pt = decrypt key text in |
|||
(key, pt) |
|||
(*** Output ***) |
|||
key = 'B' |
|||
Call test key, pt |
|||
pt = alpha |
|||
key = 'A' |
|||
Call test key, pt |
|||
pt = sampledata() |
|||
key = 'Hamlet; Prince of Denmark' |
|||
Call test key, pt |
|||
Return |
|||
End |
|||
Exit |
|||
vigenere: |
|||
Procedure Expose alpha |
|||
Do |
|||
Parse upper Arg meth, key, text |
|||
Select |
|||
When 'ENCIPHER'~abbrev(meth, 1) = 1 then df = 1 |
|||
When 'DECIPHER'~abbrev(meth, 1) = 1 then df = -1 |
|||
Otherwise Do |
|||
Say meth 'invalid. Must be "ENCIPHER" or "DECIPHER"' |
|||
Exit |
|||
End |
|||
End |
|||
text = stringscrubber(text) |
|||
key = stringscrubber(key) |
|||
code = '' |
|||
Do l_ = 1 to text~length() |
|||
M = alpha~pos(text~substr(l_, 1)) - 1 |
|||
k_ = (l_ - 1) // key~length() |
|||
K = alpha~pos(key~substr(k_ + 1, 1)) - 1 |
|||
C = mod((M + K * df), alpha~length()) |
|||
C = alpha~substr(C + 1, 1) |
|||
code = code || C |
|||
End l_ |
|||
Return code |
|||
Return |
|||
End |
|||
Exit |
|||
vigenere_encipher: |
|||
Procedure Expose alpha |
|||
Do |
|||
Parse upper Arg key, plaintext |
|||
Return vigenere('ENCIPHER', key, plaintext) |
|||
End |
|||
Exit |
|||
vigenere_decipher: |
|||
Procedure Expose alpha |
|||
Do |
|||
Parse upper Arg key, ciphertext |
|||
Return vigenere('DECIPHER', key, ciphertext) |
|||
End |
|||
Exit |
|||
mod: |
|||
Procedure |
|||
Do |
|||
Parse Arg N, D |
|||
Return (D + (N // D)) // D |
|||
End |
|||
Exit |
|||
stringscrubber: |
|||
Procedure Expose alpha |
|||
Do |
|||
Parse upper Arg cleanup |
|||
cleanup = cleanup~space(0) |
|||
Do label f_ forever |
|||
x_ = cleanup~verify(alpha) |
|||
If x_ = 0 then Leave f_ |
|||
cleanup = cleanup~changestr(cleanup~substr(x_, 1), '') |
|||
end f_ |
|||
Return cleanup |
|||
End |
|||
Exit |
|||
test: |
|||
Procedure Expose alpha |
|||
Do |
|||
Parse Arg key, pt |
|||
ct = vigenere_encipher(key, pt) |
|||
Call display ct |
|||
dt = vigenere_decipher(key, ct) |
|||
Call display dt |
|||
Return |
|||
End |
|||
Exit |
|||
display: |
|||
Procedure |
|||
Do |
|||
Parse Arg text |
|||
line = '' |
|||
o_ = 0 |
|||
Do c_ = 1 to text~length() |
|||
b_ = o_ // 5 |
|||
o_ = o_ + 1 |
|||
If b_ = 0 then line = line' ' |
|||
line = line || text~substr(c_, 1) |
|||
End c_ |
|||
Say '....+....|'~copies(8) |
|||
Do label l_ forever |
|||
Parse Var line w1 w2 w3 w4 w5 w6 W7 w8 w9 w10 w11 w12 line |
|||
pline = w1 w2 w3 w4 w5 w6 w7 w8 w9 w10 w11 w12 |
|||
Say pline~strip() |
|||
If line~strip()~length() = 0 then Leave l_ |
|||
End l_ |
|||
Say |
|||
Return |
|||
End |
|||
Exit |
|||
sampledata: |
|||
Procedure |
|||
Do |
|||
NL = '0a'x |
|||
X = 0 |
|||
antic_disposition. = '' |
|||
X = X + 1; antic_disposition.0 = X; antic_disposition.X = "To be, or not to be--that is the question:" |
|||
X = X + 1; antic_disposition.0 = X; antic_disposition.X = "Whether 'tis nobler in the mind to suffer" |
|||
X = X + 1; antic_disposition.0 = X; antic_disposition.X = "The slings and arrows of outrageous fortune" |
|||
X = X + 1; antic_disposition.0 = X; antic_disposition.X = "Or to take arms against a sea of troubles" |
|||
X = X + 1; antic_disposition.0 = X; antic_disposition.X = "And by opposing end them. To die, to sleep--" |
|||
X = X + 1; antic_disposition.0 = X; antic_disposition.X = "No more--and by a sleep to say we end" |
|||
X = X + 1; antic_disposition.0 = X; antic_disposition.X = "The heartache, and the thousand natural shocks" |
|||
X = X + 1; antic_disposition.0 = X; antic_disposition.X = "That flesh is heir to. 'Tis a consummation" |
|||
X = X + 1; antic_disposition.0 = X; antic_disposition.X = "Devoutly to be wished. To die, to sleep--" |
|||
X = X + 1; antic_disposition.0 = X; antic_disposition.X = "To sleep--perchance to dream: ay, there's the rub," |
|||
X = X + 1; antic_disposition.0 = X; antic_disposition.X = "For in that sleep of death what dreams may come" |
|||
X = X + 1; antic_disposition.0 = X; antic_disposition.X = "When we have shuffled off this mortal coil," |
|||
X = X + 1; antic_disposition.0 = X; antic_disposition.X = "Must give us pause. There's the respect" |
|||
X = X + 1; antic_disposition.0 = X; antic_disposition.X = "That makes calamity of so long life." |
|||
X = X + 1; antic_disposition.0 = X; antic_disposition.X = "For who would bear the whips and scorns of time," |
|||
X = X + 1; antic_disposition.0 = X; antic_disposition.X = "Th' oppressor's wrong, the proud man's contumely" |
|||
X = X + 1; antic_disposition.0 = X; antic_disposition.X = "The pangs of despised love, the law's delay," |
|||
X = X + 1; antic_disposition.0 = X; antic_disposition.X = "The insolence of office, and the spurns" |
|||
X = X + 1; antic_disposition.0 = X; antic_disposition.X = "That patient merit of th' unworthy takes," |
|||
X = X + 1; antic_disposition.0 = X; antic_disposition.X = "When he himself might his quietus make" |
|||
X = X + 1; antic_disposition.0 = X; antic_disposition.X = "With a bare bodkin? Who would fardels bear," |
|||
X = X + 1; antic_disposition.0 = X; antic_disposition.X = "To grunt and sweat under a weary life," |
|||
X = X + 1; antic_disposition.0 = X; antic_disposition.X = "But that the dread of something after death," |
|||
X = X + 1; antic_disposition.0 = X; antic_disposition.X = "The undiscovered country, from whose bourn" |
|||
X = X + 1; antic_disposition.0 = X; antic_disposition.X = "No traveller returns, puzzles the will," |
|||
X = X + 1; antic_disposition.0 = X; antic_disposition.X = "And makes us rather bear those ills we have" |
|||
X = X + 1; antic_disposition.0 = X; antic_disposition.X = "Than fly to others that we know not of?" |
|||
X = X + 1; antic_disposition.0 = X; antic_disposition.X = "Thus conscience does make cowards of us all," |
|||
X = X + 1; antic_disposition.0 = X; antic_disposition.X = "And thus the native hue of resolution" |
|||
X = X + 1; antic_disposition.0 = X; antic_disposition.X = "Is sicklied o'er with the pale cast of thought," |
|||
X = X + 1; antic_disposition.0 = X; antic_disposition.X = "And enterprise of great pith and moment" |
|||
X = X + 1; antic_disposition.0 = X; antic_disposition.X = "With this regard their currents turn awry" |
|||
X = X + 1; antic_disposition.0 = X; antic_disposition.X = "And lose the name of action. -- Soft you now," |
|||
X = X + 1; antic_disposition.0 = X; antic_disposition.X = "The fair Ophelia! -- Nymph, in thy orisons" |
|||
X = X + 1; antic_disposition.0 = X; antic_disposition.X = "Be all my sins remembered." |
|||
melancholy_dane = '' |
|||
Do l_ = 1 for antic_disposition.0 |
|||
melancholy_dane = melancholy_dane || antic_disposition.l_ || NL |
|||
End l_ |
|||
Return melancholy_dane |
|||
End |
|||
Exit |
|||
</lang> |
|||
let _ = |
|||
let long_text = "\ |
|||
MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH \ |
|||
VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD \ |
|||
ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS \ |
|||
FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG \ |
|||
ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ \ |
|||
ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS \ |
|||
JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT \ |
|||
LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST \ |
|||
MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH \ |
|||
QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV \ |
|||
RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW \ |
|||
TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO \ |
|||
SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR \ |
|||
ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX \ |
|||
BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB \ |
|||
BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA \ |
|||
FWAML ZZRXJ EKAHV FASMU LVVUT TGK" |
|||
in |
|||
let english_freqs = [ |
|||
0.08167; 0.01492; 0.02782; 0.04253; 0.12702; 0.02228; 0.02015; |
|||
0.06094; 0.06966; 0.00153; 0.00772; 0.04025; 0.02406; 0.06749; |
|||
0.07507; 0.01929; 0.00095; 0.05987; 0.06327; 0.09056; 0.02758; |
|||
0.00978; 0.02360; 0.00150; 0.01974; 0.00074 |
|||
] |
|||
in |
|||
let (key, pt) = cryptanalyze english_freqs long_text in |
|||
Printf.printf "Key: %s\n\nText: %s" key pt |
|||
;; |
|||
</syntaxhighlight> |
|||
{{out}} |
{{out}} |
||
<pre> |
|||
<pre style="height: 60ex; overflow: scroll;"> |
|||
Key: THECHESHIRECAT |
|||
....+....|....+....|....+....|....+....|....+....|....+....|....+....|....+....| |
|||
LXFOP VEFRN HR |
|||
Text: THISWASTHEPOEMTHATALICEREADJABBERWOC... |
|||
....+....|....+....|....+....|....+....|....+....|....+....|....+....|....+....| |
|||
ATTAC KATDA WN |
|||
....+....|....+....|....+....|....+....|....+....|....+....|....+....|....+....| |
|||
NGGNP XNGQN JA |
|||
....+....|....+....|....+....|....+....|....+....|....+....|....+....|....+....| |
|||
ATTAC KATDA WN |
|||
....+....|....+....|....+....|....+....|....+....|....+....|....+....|....+....| |
|||
BUUBD LBUEB XO |
|||
....+....|....+....|....+....|....+....|....+....|....+....|....+....|....+....| |
|||
ATTAC KATDA WN |
|||
....+....|....+....|....+....|....+....|....+....|....+....|....+....|....+....| |
|||
ABCDE FGHIJ KLMNO PQRST UVWXY Z |
|||
....+....|....+....|....+....|....+....|....+....|....+....|....+....|....+....| |
|||
ABCDE FGHIJ KLMNO PQRST UVWXY Z |
|||
....+....|....+....|....+....|....+....|....+....|....+....|....+....|....+....| |
|||
AONPS KCFBG QFSYK EGUSK RLQGP WMXFV JJIHM HVGUS EYILQ CMGIY MZKRR YRWHR |
|||
FVBAH QDPBC XANPH OWUSJ EOWYB TDLKX DLASQ VHZQI BDTFD HKQLV FHRON KRGYD |
|||
WRMOW DYOGM PXHRV QDCCU SSFUN XOUDF SIFIF LVGXC XOIRB NFWVR QLRWQ PIFNI |
|||
SUWSF MYNOL NPELX WVIEV EQMHE APTYO AHAFW TCUVN VYFFO WUACB CAHME JETJP |
|||
VULSN UXBFI JKJOZ DYFBR BVQRR JYSHF LPDVB QHMLW VLGQH WJWSF XEVZA OEWIX |
|||
EGMEE LOSFI GADIO HMMJX ATIMF VLSWX FSARZ XAHME WETVX BHHSF WLJTA KNYEM |
|||
XWFPP KBOIK MHRJQ HRFLS TFJYA VLBHJ HMLWZ ARKKS CATPF JJBTK ZSZVT NGSVD |
|||
OEDPW MWVZR UTSHW XUMTD KREEN EEPDQ GASTX RPBZG CSMER ZVPWF EBWPR GHEEF |
|||
HVGOI BDEGS JKBTR GTIXV YEKRV PBCIL HFZFY VCSJX UQPIY BDYLR LRFVG WQSQB |
|||
XUQPR XNSAQ HXHGQ FGHZT YIGTE CKDSP PPTNK PRKRG TOIAO EFPVF RTGXP ELGJI |
|||
GUXVA ETYKA PMEMX CKURT MHTIX UGNNY YTTJX TZVAJ JIBMH LVYSV VMMUR LMWZA |
|||
DWMSY XWZMK VGPTT LFTGV JBFOW SZLBI OLVKF MCHXA JJRCV HTJVH ZTRXK SIPEM |
|||
JELRT EKJDV LXIWO IUFEL TIKPR FVSFG SSEOD OAHUY KTUKM EFIOY KXUQU ENPSO |
|||
ZZXGV LPQYB YUCSD ODGOO EPFHJ IVAQX FFYIY XEIBL TGCRL ELHMN IGYKI JULCK |
|||
UDYLO XHLAE CXVJU FRMRK RVSQT PEHNM UCZSY KEARL PDVOF SIKHK PNVAS PQSJZ |
|||
OKYMT TFWVD EAPKI BHHHB QSDKR EOZAT GUABH YGFOP NZDKR BSFSI GPKQI GLIJR |
|||
JEQSF VBTUZ RBHJQ PMPWJ GSRDW ZDOTT PTTAV KNUXC KWLBG GYDHN PPRMT IXEKW |
|||
STIKE QAKZP TTLRW BFURP XKNWL GTIJB LGMCH MWVQE EYFWH RGETL BUAIC CTCUT |
|||
BUIHM HRNYE FPHCF TSGHF NGASI SRAGT EWKPR AALXA ZIAAQ DMLRG TYFBP SAYWU |
|||
TRTYO CGNQW EQMVW IEDPH |
|||
....+....|....+....|....+....|....+....|....+....|....+....|....+....|....+....| |
|||
TOBEO RNOTT OBETH ATIST HEQUE STION WHETH ERTIS NOBLE RINTH EMIND TOSUF |
|||
FERTH ESLIN GSAND ARROW SOFOU TRAGE OUSFO RTUNE ORTOT AKEAR MSAGA INSTA |
|||
SEAOF TROUB LESAN DBYOP POSIN GENDT HEMTO DIETO SLEEP NOMOR EANDB YASLE |
|||
EPTOS AYWEE NDTHE HEART ACHEA NDTHE THOUS ANDNA TURAL SHOCK STHAT FLESH |
|||
ISHEI RTOTI SACON SUMMA TIOND EVOUT LYTOB EWISH EDTOD IETOS LEEPT OSLEE |
|||
PPERC HANCE TODRE AMAYT HERES THERU BFORI NTHAT SLEEP OFDEA THWHA TDREA |
|||
MSMAY COMEW HENWE HAVES HUFFL EDOFF THISM ORTAL COILM USTGI VEUSP AUSET |
|||
HERES THERE SPECT THATM AKESC ALAMI TYOFS OLONG LIFEF ORWHO WOULD BEART |
|||
HEWHI PSAND SCORN SOFTI METHO PPRES SORSW RONGT HEPRO UDMAN SCONT UMELY |
|||
THEPA NGSOF DESPI SEDLO VETHE LAWSD ELAYT HEINS OLENC EOFOF FICEA NDTHE |
|||
SPURN STHAT PATIE NTMER ITOFT HUNWO RTHYT AKESW HENHE HIMSE LFMIG HTHIS |
|||
QUIET USMAK EWITH ABARE BODKI NWHOW OULDF ARDEL SBEAR TOGRU NTAND SWEAT |
|||
UNDER AWEAR YLIFE BUTTH ATTHE DREAD OFSOM ETHIN GAFTE RDEAT HTHEU NDISC |
|||
OVERE DCOUN TRYFR OMWHO SEBOU RNNOT RAVEL LERRE TURNS PUZZL ESTHE WILLA |
|||
NDMAK ESUSR ATHER BEART HOSEI LLSWE HAVET HANFL YTOOT HERST HATWE KNOWN |
|||
OTOFT HUSCO NSCIE NCEDO ESMAK ECOWA RDSOF USALL ANDTH USTHE NATIV EHUEO |
|||
FRESO LUTIO NISSI CKLIE DOERW ITHTH EPALE CASTO FTHOU GHTAN DENTE RPRIS |
|||
EOFGR EATPI THAND MOMEN TWITH THISR EGARD THEIR CURRE NTSTU RNAWR YANDL |
|||
OSETH ENAME OFACT IONSO FTYOU NOWTH EFAIR OPHEL IANYM PHINT HYORI SONSB |
|||
EALLM YSINS REMEM BERED |
|||
</pre> |
</pre> |
||
=={{header| |
=={{header|Perl}}== |
||
<syntaxhighlight lang="perl">use strict; |
|||
<lang pascal> |
|||
use warnings; |
|||
// The Vigenere cipher in reasonably standard Pascal |
|||
use feature 'say'; |
|||
// <no library functions: all conversions hand-coded> |
|||
PROGRAM Vigenere; |
|||
# from Wikipedia |
|||
// get a letter's alphabetic position (A=0) |
|||
my %English_letter_freq = ( |
|||
FUNCTION letternum(letter: CHAR): BYTE; |
|||
E => 12.70, L => 4.03, Y => 1.97, P => 1.93, T => 9.06, A => 8.17, O => 7.51, I => 6.97, N => 6.75, |
|||
BEGIN |
|||
S => 6.33, H => 6.09, R => 5.99, D => 4.25, C => 2.78, U => 2.76, M => 2.41, W => 2.36, F => 2.23, |
|||
letternum := (ord(letter)-ord('A')); |
|||
G => 2.02, B => 1.29, V => 0.98, K => 0.77, J => 0.15, X => 0.15, Q => 0.10, Z => 0.07 |
|||
END; |
|||
); |
|||
my @alphabet = sort keys %English_letter_freq; |
|||
my $max_key_lengths = 5; # number of keylengths to try |
|||
sub myguess { |
|||
// convert a character to uppercase |
|||
my ($text) = (@_); |
|||
FUNCTION uch(ch: CHAR): CHAR; |
|||
my ($seqtext, @spacing, @factors, @sortedfactors, $pos, %freq, %Keys); |
|||
BEGIN |
|||
uch := ch; |
|||
IF ch IN ['a'..'z'] THEN |
|||
uch := chr(ord(ch) AND $5F); |
|||
END; |
|||
// convert a string to uppercase |
|||
FUNCTION ucase(str: STRING): STRING; |
|||
VAR i: BYTE; |
|||
BEGIN |
|||
ucase := ''; |
|||
FOR i := 1 TO Length(str) DO |
|||
ucase := ucase + uch(str[i]); |
|||
END; |
|||
// construct a Vigenere-compatible string: |
|||
// uppercase; no spaces or punctuation. |
|||
FUNCTION vstr(pt: STRING): STRING; |
|||
VAR c: Cardinal; |
|||
s: STRING; |
|||
BEGIN |
|||
vstr:= ''; |
|||
s := ucase(pt); |
|||
FOR c := 1 TO Length(s) DO BEGIN |
|||
IF s[c] IN ['A'..'Z'] THEN |
|||
vstr += s[c]; |
|||
END; |
|||
END; |
|||
// construct a repeating Vigenere key |
|||
FUNCTION vkey(pt, key: STRING): STRING; |
|||
VAR c,n: Cardinal; |
|||
k : STRING; |
|||
BEGIN |
|||
k := vstr(key); |
|||
vkey := ''; |
|||
FOR c := 1 TO Length(pt) DO BEGIN |
|||
n := c mod Length(k); |
|||
IF n>0 THEN vkey += k[n] ELSE vkey += k[Length(k)]; |
|||
END; |
|||
END; |
|||
// Vigenere encipher |
|||
FUNCTION enVig(pt,key:STRING): STRING; |
|||
VAR ct: STRING; |
|||
c,n : Cardinal; |
|||
BEGIN |
|||
ct := pt; |
|||
FOR c := 1 TO Length(pt) DO BEGIN |
|||
n := letternum(pt[c])+letternum(key[c]); |
|||
n := n mod 26; |
|||
ct[c]:=chr(ord('A')+n); |
|||
END; |
|||
enVig := ct; |
|||
END; |
|||
// Vigenere decipher |
|||
FUNCTION deVig(ct,key:STRING): STRING; |
|||
VAR pt : STRING; |
|||
c,n : INTEGER; |
|||
BEGIN |
|||
pt := ct; |
|||
FOR c := 1 TO Length(ct) DO BEGIN |
|||
n := letternum(ct[c])-letternum(key[c]); |
|||
IF n<0 THEN n:=26+n; |
|||
pt[c]:=chr(ord('A')+n); |
|||
END; |
|||
deVig := pt; |
|||
END; |
|||
# Kasiski examination |
|||
$seqtext = $text; |
|||
VAR key: STRING = 'Vigenere cipher'; |
|||
while ($seqtext =~ /(...).*\1/) { |
|||
msg: STRING = 'Beware the Jabberwock! The jaws that bite, the claws that catch!'; |
|||
$seqtext = substr($seqtext, 1+index($seqtext, $1)); |
|||
vtx: STRING = ''; |
|||
push @spacing, 1 + index($seqtext, $1); |
|||
ctx: STRING = ''; |
|||
} |
|||
ptx: STRING = ''; |
|||
for my $j (@spacing) { |
|||
BEGIN |
|||
push @factors, grep { $j % $_ == 0 } 2..$j; |
|||
// make Vigenere-compatible |
|||
} |
|||
vtx := vstr(msg); |
|||
$freq{$_}++ for @factors; |
|||
key := vkey(vtx,key); |
|||
@sortedfactors = grep { $_ >= 4 } sort { $freq{$b} <=> $freq{$a} } keys %freq; # discard very short keys |
|||
// Vigenere encipher / decipher |
|||
ctx := enVig(vtx,key); |
|||
ptx := deVig(ctx,key); |
|||
// display results |
|||
Writeln('Message : ',msg); |
|||
Writeln('Plaintext : ',vtx); |
|||
Writeln('Key : ',key); |
|||
Writeln('Ciphertext : ',ctx); |
|||
Writeln('Plaintext : ',ptx); |
|||
END. |
|||
for my $keylen ( @sortedfactors[0..$max_key_lengths-1] ) { |
|||
</lang> |
|||
my $keyguess = ''; |
|||
for (my $i = 0; $i < $keylen; $i++) { |
|||
my($mykey, %chi_values, $bestguess); |
|||
for (my $j = 0; $j < length($text); $j += $keylen) { |
|||
$mykey .= substr($text, ($j+$i) % length($text), 1); |
|||
} |
|||
for my $subkey (@alphabet) { |
|||
{{out}} |
|||
my $decrypted = mycrypt($mykey, $subkey); |
|||
<pre> |
|||
my $length = length($decrypted); |
|||
Message : Beware the Jabberwock! The jaws that bite, the claws that catch! |
|||
for my $char (@alphabet) { |
|||
Plaintext : BEWARETHEJABBERWOCKTHEJAWSTHATBITETHECLAWSTHATCATCH |
|||
my $expected = $English_letter_freq{$char} * $length / 100; |
|||
Key : VIGENERECIPHERVIGENERECIPHERVIGENERECIPHERVIGENEREC |
|||
my $observed; |
|||
Ciphertext : WMCEEIKLGRPIFVMEUGXXYILILZXYVBHMGIKLGKAHAJOPGXPEKGJ |
|||
++$observed while $decrypted =~ /$char/g; |
|||
Plaintext : BEWARETHEJABBERWOCKTHEJAWSTHATBITETHECLAWSTHATCATCH |
|||
$chi_values{$subkey} += ($observed - $expected)**2 / $expected if $observed; |
|||
</pre> |
|||
} |
|||
} |
|||
$Keys{$keylen}{score} = $chi_values{'A'}; |
|||
=={{header|Perl}}== |
|||
for my $sk (sort keys %chi_values) { |
|||
<lang perl>if( @ARGV != 3 ){ |
|||
if ($chi_values{$sk} <= $Keys{$keylen}{score}) { |
|||
printHelp(); |
|||
$bestguess = $sk; |
|||
$Keys{$keylen}{score} = $chi_values{$sk}; |
|||
} |
|||
} |
|||
$keyguess .= $bestguess; |
|||
} |
|||
$Keys{$keylen}{key} = $keyguess; |
|||
} |
|||
map { $Keys{$_}{key} } sort { $Keys{$a}{score} <=> $Keys{$b}{score}} keys %Keys; |
|||
} |
} |
||
sub mycrypt { |
|||
# translate to upper-case, remove anything else |
|||
my ($text, $key) = @_; |
|||
map( (tr/a-z/A-Z/, s/[^A-Z]//g), @ARGV ); |
|||
my ($new_text, %values_numbers); |
|||
my $ |
my $keylen = length($key); |
||
@values_numbers{@alphabet} = 0..25; |
|||
my %values_letters = reverse %values_numbers; |
|||
for (my $i = 0; $i < length($text); $i++) { |
|||
if( $cipher_decipher !~ /ENC|DEC/ ){ |
|||
my $val = -1 * $values_numbers{substr( $key, $i%$keylen, 1)} # negative shift for decode |
|||
printHelp(); # user should say what to do |
|||
+ $values_numbers{substr($text, $i, 1)}; |
|||
$new_text .= $values_letters{ $val % 26 }; |
|||
} |
|||
return $new_text; |
|||
} |
} |
||
my $cipher_text = <<~'EOD'; |
|||
print "Key: " . (my $key = $ARGV[ 2 ]) . "\n"; |
|||
MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH |
|||
VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD |
|||
ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS |
|||
FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG |
|||
ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ |
|||
ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS |
|||
JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT |
|||
LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST |
|||
MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH |
|||
QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV |
|||
RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW |
|||
TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO |
|||
SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR |
|||
ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX |
|||
BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB |
|||
BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA |
|||
FWAML ZZRXJ EKAHV FASMU LVVUT TGK |
|||
EOD |
|||
my $text = uc($cipher_text) =~ s/[^@{[join '', @alphabet]}]//gr; |
|||
if( $cipher_decipher =~ /ENC/ ){ |
|||
print "Plain-text: " . (my $plain = $ARGV[ 1 ]) . "\n"; |
|||
print "Encrypted: " . Vigenere( 1, $key, $plain ) . "\n"; |
|||
}elsif( $cipher_decipher =~ /DEC/ ){ |
|||
print "Cipher-text: " . (my $cipher = $ARGV[ 1 ]) . "\n"; |
|||
print "Decrypted: " . Vigenere( -1, $key, $cipher ) . "\n"; |
|||
} |
|||
for my $key ( myguess($text) ) { |
|||
sub printHelp{ |
|||
say "Key $key\n" . |
|||
" |
"Key length " . length($key) . "\n" . |
||
" |
"Plaintext " . substr(mycrypt($text, $key), 0, 80) . "...\n"; |
||
}</syntaxhighlight> |
|||
exit -1; |
|||
} |
|||
{{out}} |
|||
sub Vigenere{ |
|||
<pre>Key THECHESHIRECAT |
|||
my ($direction, $key, $text) = @_; |
|||
Key length 14 |
|||
for( my $count = 0; $count < length $text; $count ++ ){ |
|||
Plaintext THISWASTHEPOEMTHATALICEREADJABBERWOCKYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIMB... |
|||
$key_offset = $direction * ord substr( $key, $count % length $key, 1); |
|||
$char_offset = ord substr( $text, $count, 1 ); |
|||
$cipher .= chr 65 + ((($char_offset % 26) + ($key_offset % 26)) % 26); |
|||
# 65 is the ASCII character code for 'A' |
|||
} |
|||
return $cipher; |
|||
}</lang> |
|||
Key THECHESCIRECATTHECHESHIRECAT |
|||
Demonstration: |
|||
Key length 28 |
|||
Plaintext THISWASYHEPOEMTHATALICEREADJABBERWOHKYTWASBRILLIGANDTHESLITHYTOAESDIDGYREANDGIMB... |
|||
Key TJGGAHET |
|||
<pre>$ perl cipher.pl ENC 'Beware the Jabberwock, my son! The jaws that bite, the claws that catch!' VIGENERECIPHER |
|||
Key |
Key length 8 |
||
Plaintext TFGODXGHWMNKEYIVLMBJACIPPTXWTBBNFRADSITFHCOSMGOTFYPOXCASLGRDFQCJTABEDSNFPTOBYIQZ... |
|||
Plain-text: BEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCH |
|||
Encrypted: WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY |
|||
Key THECSAS |
|||
$ perl cipher.pl DEC WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY VIGENERECIPHER |
|||
Key |
Key length 7 |
||
Plaintext THISLESHIRRYENTHATPPIQFEGKDKABBEGAOQLLVGATBRILAMGOOQVRETLITHNXOJFFFSDHYREACHGWNO... |
|||
Cipher-text: WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY |
|||
Decrypted: BEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCH |
|||
Key THEC |
|||
$ perl cipher.pl FOO WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY VIGENERECIPHER |
|||
Key length 4 |
|||
Usage: |
|||
Plaintext THISKXGYWOPOLYIMLODNHCIGPVZAABBEFTCHZITWHEQWTGOKFARSECAJLITHMQCATCDIKSNWPVQFFIQQ...</pre> |
|||
Encrypting: |
|||
perl cipher.pl ENC (plain text) (key) |
|||
Decrypting: |
|||
perl cipher.pl DEC (cipher text) (key)</pre> |
|||
=={{header|Phix}}== |
=={{header|Phix}}== |
||
{{trans|Julia}} |
|||
<lang Phix>enum type mode ENCRYPT = +1, DECRYPT = -1 end type |
|||
<!--<syntaxhighlight lang="phix">(phixonline)--> |
|||
<span style="color: #000080;font-style:italic;">-- |
|||
function Vigenere(string s, string key, mode m) |
|||
-- demo\rosetta\Cryptanalysis.exw |
|||
string res = "" |
|||
--</span> |
|||
integer k = 1, ch |
|||
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span> |
|||
s = upper(s) |
|||
<span style="color: #004080;">atom</span> <span style="color: #000000;">t0</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">time</span><span style="color: #0000FF;">()</span> |
|||
for i=1 to length(s) do |
|||
<span style="color: #008080;">constant</span> <span style="color: #000000;">ciphertext</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">substitute_all</span><span style="color: #0000FF;">(</span><span style="color: #008000;">""" |
|||
ch = s[i] |
|||
MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH |
|||
if ch>='A' and ch<='Z' then |
|||
VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD |
|||
res &= 'A'+mod(ch+m*(key[k]+26),26) |
|||
ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS |
|||
k = mod(k,length(key))+1 |
|||
FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG |
|||
end if |
|||
ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ |
|||
end for |
|||
ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS |
|||
return res |
|||
JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT |
|||
end function |
|||
LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST |
|||
MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH |
|||
QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV |
|||
RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW |
|||
TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO |
|||
SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR |
|||
ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX |
|||
BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB |
|||
BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA |
|||
FWAML ZZRXJ EKAHV FASMU LVVUT TGK"""</span><span style="color: #0000FF;">,{</span><span style="color: #008000;">" "</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"\n"</span><span style="color: #0000FF;">},{</span><span style="color: #008000;">""</span><span style="color: #0000FF;">,</span><span style="color: #008000;">""</span><span style="color: #0000FF;">})</span> |
|||
<span style="color: #008080;">constant</span> <span style="color: #000000;">letters</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">new_dict</span><span style="color: #0000FF;">(</span> |
|||
constant key = "LEMON", |
|||
<span style="color: #0000FF;">{{</span><span style="color: #008000;">'E'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">12.702</span><span style="color: #0000FF;">},</span> |
|||
s = "ATTACK AT DAWN", |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'T'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">9.056</span><span style="color: #0000FF;">},</span> |
|||
e = Vigenere(s,key,ENCRYPT), |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'A'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">8.167</span><span style="color: #0000FF;">},</span> |
|||
d = Vigenere(e,key,DECRYPT) |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'O'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">7.507</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'I'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">6.966</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'N'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">6.749</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'S'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">6.327</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'H'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">6.094</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'R'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">5.987</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'D'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">4.253</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'L'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">4.025</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'C'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.782</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'U'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.758</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'M'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.406</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'W'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.361</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'F'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.228</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'G'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.015</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'Y'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1.974</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'P'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1.929</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'B'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1.492</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'V'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0.978</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'K'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0.772</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'J'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0.153</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'X'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0.150</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'Q'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0.095</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'Z'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0.074</span><span style="color: #0000FF;">}})</span> |
|||
<span style="color: #008080;">constant</span> <span style="color: #000000;">digraphs</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">new_dict</span><span style="color: #0000FF;">(</span> |
|||
<span style="color: #0000FF;">{{</span><span style="color: #008000;">"TH"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">15.2</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"HE"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">12.8</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"IN"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">9.4</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"ER"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">9.4</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"AN"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">8.2</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"RE"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">6.8</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"ND"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">6.3</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"AT"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">5.9</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"ON"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">5.7</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"NT"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">5.6</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"HA"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">5.6</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"ES"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">5.6</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"ST"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">5.5</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"EN"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">5.5</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"ED"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">5.3</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"TO"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">5.2</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"IT"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">5.0</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"OU"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">5.0</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"EA"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">4.7</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"HI"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">4.6</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"IS"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">4.6</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"OR"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">4.3</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"TI"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">3.4</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"AS"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">3.3</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"TE"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.7</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"ET"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1.9</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"NG"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1.8</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"OF"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1.6</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"AL"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0.9</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"DE"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0.9</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"SE"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0.8</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"LE"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0.8</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"SA"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0.6</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"SI"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0.5</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"AR"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0.4</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"VE"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0.4</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"RA"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0.4</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"LD"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0.2</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"UR"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0.2</span><span style="color: #0000FF;">}})</span> |
|||
<span style="color: #008080;">constant</span> <span style="color: #000000;">trigraphs</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">new_dict</span><span style="color: #0000FF;">(</span> |
|||
<span style="color: #0000FF;">{{</span><span style="color: #008000;">"THE"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">18.1</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"AND"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">7.3</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"ING"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">7.2</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"ION"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">4.2</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"ENT"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">4.2</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"HER"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">3.6</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"FOR"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">3.4</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"THA"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">3.3</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"NTH"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">3.3</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"INT"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">3.2</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"TIO"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">3.1</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"ERE"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">3.1</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"TER"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">3.0</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"EST"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.8</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"ERS"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.8</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"HAT"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.6</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"ATI"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.6</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"ATE"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.5</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"ALL"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.5</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"VER"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.4</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"HIS"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.4</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"HES"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.4</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"ETH"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.4</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"OFT"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.2</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"STH"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.1</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"RES"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.1</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"OTH"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.1</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"ITH"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.1</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"FTH"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.1</span><span style="color: #0000FF;">},</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"ONT"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.0</span><span style="color: #0000FF;">}})</span> |
|||
<span style="color: #008080;">function</span> <span style="color: #000000;">decrypt</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">enc</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">string</span> <span style="color: #000000;">key</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #004080;">integer</span> <span style="color: #000000;">keylen</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">key</span><span style="color: #0000FF;">),</span> <span style="color: #000000;">k</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">1</span> |
|||
<span style="color: #004080;">string</span> <span style="color: #000000;">msg</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #008000;">' '</span><span style="color: #0000FF;">,</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">enc</span><span style="color: #0000FF;">))</span> |
|||
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">enc</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span> |
|||
<span style="color: #000000;">msg</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">mod</span><span style="color: #0000FF;">(</span><span style="color: #000000;">enc</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]-</span><span style="color: #000000;">key</span><span style="color: #0000FF;">[</span><span style="color: #000000;">k</span><span style="color: #0000FF;">]+</span><span style="color: #000000;">26</span><span style="color: #0000FF;">,</span><span style="color: #000000;">26</span><span style="color: #0000FF;">)+</span><span style="color: #008000;">'A'</span> |
|||
<span style="color: #000000;">k</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">mod</span><span style="color: #0000FF;">(</span><span style="color: #000000;">k</span><span style="color: #0000FF;">,</span><span style="color: #000000;">keylen</span><span style="color: #0000FF;">)+</span><span style="color: #000000;">1</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span> |
|||
<span style="color: #008080;">return</span> <span style="color: #000000;">msg</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span> |
|||
<span style="color: #008080;">function</span> <span style="color: #000000;">cryptanalyze</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">enc</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">integer</span> <span style="color: #000000;">maxkeylen</span><span style="color: #0000FF;">=</span><span style="color: #000000;">20</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #004080;">integer</span> <span style="color: #000000;">enclen</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">enc</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #004080;">string</span> <span style="color: #000000;">maxkey</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">""</span><span style="color: #0000FF;">,</span> |
|||
<span style="color: #000000;">maxdec</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">""</span><span style="color: #0000FF;">,</span> |
|||
<span style="color: #000000;">k1</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">" "</span> |
|||
<span style="color: #004080;">atom</span> <span style="color: #000000;">maxscore</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0.0</span> |
|||
<span style="color: #008080;">for</span> <span style="color: #000000;">keylen</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #000000;">maxkeylen</span> <span style="color: #008080;">do</span> |
|||
<span style="color: #004080;">string</span> <span style="color: #000000;">key</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #008000;">' '</span><span style="color: #0000FF;">,</span><span style="color: #000000;">keylen</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #004080;">sequence</span> <span style="color: #000000;">idx</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{}</span> |
|||
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #000000;">enclen</span> <span style="color: #008080;">do</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #7060A8;">mod</span><span style="color: #0000FF;">(</span><span style="color: #000000;">i</span><span style="color: #0000FF;">,</span><span style="color: #000000;">keylen</span><span style="color: #0000FF;">)=</span><span style="color: #000000;">0</span> <span style="color: #008080;">then</span> |
|||
<span style="color: #000000;">idx</span> <span style="color: #0000FF;">&=</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">-</span><span style="color: #000000;">keylen</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span> |
|||
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #000000;">keylen</span> <span style="color: #008080;">do</span> |
|||
printf(1,"Original: %s\nEncrypted: %s\nDecrypted: %s\n",{s,e,d})</lang> |
|||
<span style="color: #004080;">atom</span> <span style="color: #000000;">maxsubscore</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0.0</span> |
|||
<span style="color: #008080;">for</span> <span style="color: #000000;">j</span><span style="color: #0000FF;">=</span><span style="color: #008000;">'A'</span> <span style="color: #008080;">to</span> <span style="color: #008000;">'Z'</span> <span style="color: #008080;">do</span> |
|||
<span style="color: #004080;">atom</span> <span style="color: #000000;">subscore</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0.0</span> |
|||
<span style="color: #000000;">k1</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">j</span> |
|||
<span style="color: #004080;">string</span> <span style="color: #000000;">encidx</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">""</span> |
|||
<span style="color: #008080;">for</span> <span style="color: #000000;">ii</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">idx</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span> |
|||
<span style="color: #000000;">encidx</span> <span style="color: #0000FF;">&=</span> <span style="color: #000000;">enc</span><span style="color: #0000FF;">[</span><span style="color: #000000;">idx</span><span style="color: #0000FF;">[</span><span style="color: #000000;">ii</span><span style="color: #0000FF;">]]</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span> |
|||
<span style="color: #004080;">string</span> <span style="color: #000000;">dec</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">decrypt</span><span style="color: #0000FF;">(</span><span style="color: #000000;">encidx</span><span style="color: #0000FF;">,</span><span style="color: #000000;">k1</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">for</span> <span style="color: #000000;">di</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">dec</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span> |
|||
<span style="color: #000000;">subscore</span> <span style="color: #0000FF;">+=</span> <span style="color: #7060A8;">getd</span><span style="color: #0000FF;">(</span><span style="color: #000000;">dec</span><span style="color: #0000FF;">[</span><span style="color: #000000;">di</span><span style="color: #0000FF;">],</span><span style="color: #000000;">letters</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">subscore</span> <span style="color: #0000FF;">></span> <span style="color: #000000;">maxsubscore</span> <span style="color: #008080;">then</span> |
|||
<span style="color: #000000;">maxsubscore</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">subscore</span> |
|||
<span style="color: #000000;">key</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">j</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span> |
|||
<span style="color: #000000;">idx</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">sq_add</span><span style="color: #0000FF;">(</span><span style="color: #000000;">idx</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span> |
|||
<span style="color: #004080;">string</span> <span style="color: #000000;">dec</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">decrypt</span><span style="color: #0000FF;">(</span><span style="color: #000000;">enc</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">key</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #004080;">atom</span> <span style="color: #000000;">score</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0.0</span> |
|||
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">dec</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span> |
|||
<span style="color: #000000;">score</span> <span style="color: #0000FF;">+=</span> <span style="color: #7060A8;">getd</span><span style="color: #0000FF;">(</span><span style="color: #000000;">dec</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">],</span><span style="color: #000000;">letters</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span> |
|||
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #000000;">enclen</span> <span style="color: #0000FF;">-</span> <span style="color: #000000;">2</span> <span style="color: #008080;">do</span> |
|||
<span style="color: #004080;">string</span> <span style="color: #000000;">digraph</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">dec</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">..</span><span style="color: #000000;">i</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]</span> |
|||
<span style="color: #004080;">string</span> <span style="color: #000000;">trigraph</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">dec</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">..</span><span style="color: #000000;">i</span> <span style="color: #0000FF;">+</span> <span style="color: #000000;">2</span><span style="color: #0000FF;">]</span> |
|||
<span style="color: #000000;">score</span> <span style="color: #0000FF;">+=</span> <span style="color: #000000;">2</span> <span style="color: #0000FF;">*</span> <span style="color: #7060A8;">getd</span><span style="color: #0000FF;">(</span><span style="color: #000000;">digraph</span><span style="color: #0000FF;">,</span><span style="color: #000000;">digraphs</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000000;">score</span> <span style="color: #0000FF;">+=</span> <span style="color: #000000;">3</span> <span style="color: #0000FF;">*</span> <span style="color: #7060A8;">getd</span><span style="color: #0000FF;">(</span><span style="color: #000000;">trigraph</span><span style="color: #0000FF;">,</span><span style="color: #000000;">trigraphs</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">score</span> <span style="color: #0000FF;">></span> <span style="color: #000000;">maxscore</span> <span style="color: #008080;">then</span> |
|||
<span style="color: #000000;">maxscore</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">score</span> |
|||
<span style="color: #000000;">maxkey</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">key</span> |
|||
<span style="color: #000000;">maxdec</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">dec</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span> |
|||
<span style="color: #008080;">return</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">maxkey</span><span style="color: #0000FF;">,</span><span style="color: #000000;">maxdec</span><span style="color: #0000FF;">}</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span> |
|||
<span style="color: #008080;">function</span> <span style="color: #000000;">fold</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">s</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">integer</span> <span style="color: #000000;">w</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">w</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">s</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">by</span> <span style="color: #000000;">w</span> <span style="color: #008080;">do</span> |
|||
<span style="color: #000000;">s</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">..</span><span style="color: #000000;">i</span><span style="color: #0000FF;">-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">"\n"</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span> |
|||
<span style="color: #008080;">return</span> <span style="color: #000000;">s</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span> |
|||
<span style="color: #004080;">string</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">key</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">dec</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">cryptanalyze</span><span style="color: #0000FF;">(</span><span style="color: #000000;">ciphertext</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"key: %s\n\n%s\n\n"</span><span style="color: #0000FF;">,</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">key</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">fold</span><span style="color: #0000FF;">(</span><span style="color: #000000;">dec</span><span style="color: #0000FF;">,</span><span style="color: #000000;">80</span><span style="color: #0000FF;">)})</span> |
|||
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"elapsed time: %3.2f seconds"</span><span style="color: #0000FF;">,{</span><span style="color: #7060A8;">time</span><span style="color: #0000FF;">()-</span><span style="color: #000000;">t0</span><span style="color: #0000FF;">})</span> |
|||
<span style="color: #0000FF;">{}</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">wait_key</span><span style="color: #0000FF;">()</span> |
|||
<!--</syntaxhighlight>--> |
|||
{{Out}} |
{{Out}} |
||
<pre> |
<pre> |
||
key: THECHESHIRECAT |
|||
Original: ATTACK AT DAWN |
|||
Encrypted: LXFOPVEFRNHR |
|||
Decrypted: ATTACKATDAWN |
|||
</pre> |
|||
THISWASTHEPOEMTHATALICEREADJABBERWOCKYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIM |
|||
=={{header|PHP}}== |
|||
BLEINTHEWABEALLMIMSYWERETHEBOROGOVESANDTHEMOMERATHSOUTGRABEBEWARETHEJABBERWOCKM |
|||
{{trans|C}} |
|||
YSONTHEJAWSTHATBITETHECLAWSTHATCATCHBEWARETHEJUBJUBBIRDANDSHUNTHEFRUMIOUSBANDER |
|||
<lang PHP><?php |
|||
SNATCHHETOOKHISVORPALSWORDINHANDLONGTIMETHEMANXOMEFOEHESOUGHTSORESTEDHEBYTHETUM |
|||
TUMTREEANDSTOODAWHILEINTHOUGHTANDASINUFFISHTHOUGHTHESTOODTHEJABBERWOCKWITHEYESO |
|||
FFLAMECAMEWHIFFLINGTHROUGHTHETULGEYWOODANDBURBLEDASITCAMEONETWOONETWOANDTHROUGH |
|||
ANDTHROUGHTHEVORPALBLADEWENTSNICKERSNACKHELEFTITDEADANDWITHITSHEADHEWENTGALUMPH |
|||
INGBACKANDHASTTHOUSLAINTHEJABBERWOCKCOMETOMYARMSMYBEAMISHBOYOFRABJOUSDAYCALLOOH |
|||
CALLAYHECHORTLEDINHISJOYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIMBLEINTHEWABEAL |
|||
LMIMSYWERETHEBOROGOVESANDTHEMOMERATHSOUTGRABEITSEEMSVERYPRETTYSHESAIDWHENSHEHAD |
|||
FINISHEDITBUTITSRATHERHARDTOUNDERSTAND |
|||
elapsed time: 0.42 seconds |
|||
$str = "Beware the Jabberwock, my son! The jaws that bite, the claws that catch!"; |
|||
</pre> |
|||
$key = "VIGENERECIPHER"; |
|||
=={{header|Python}}== |
|||
printf("Text: %s\n", $str); |
|||
{{trans|D}} |
|||
printf("key: %s\n", $key); |
|||
<syntaxhighlight lang="python">from string import uppercase |
|||
from operator import itemgetter |
|||
def vigenere_decrypt(target_freqs, input): |
|||
$cod = encipher($str, $key, true); printf("Code: %s\n", $cod); |
|||
nchars = len(uppercase) |
|||
$dec = encipher($cod, $key, false); printf("Back: %s\n", $dec); |
|||
ordA = ord('A') |
|||
sorted_targets = sorted(target_freqs) |
|||
def frequency(input): |
|||
function encipher($src, $key, $is_encode) |
|||
result = [[c, 0.0] for c in uppercase] |
|||
{ |
|||
for c in input: |
|||
result[c - ordA][1] += 1 |
|||
$src = strtoupper($src); |
|||
return result |
|||
def correlation(input): |
|||
/* strip out non-letters */ |
|||
result = 0.0 |
|||
freq = frequency(input) |
|||
freq.sort(key=itemgetter(1)) |
|||
$dest .= $char; |
|||
} |
|||
} |
|||
for i, f in enumerate(freq): |
|||
result += f[1] * sorted_targets[i] |
|||
$char = substr($dest, $i, 1); |
|||
return result |
|||
continue; |
|||
} |
|||
$dest = substr_replace($dest, |
|||
chr ( |
|||
ord('A') + |
|||
($is_encode |
|||
? ord($char) - ord('A') + ord($key[$i % strlen($key)]) - ord('A') |
|||
: ord($char) - ord($key[$i % strlen($key)]) + 26 |
|||
) % 26 |
|||
) |
|||
, $i, 1); |
|||
} |
|||
cleaned = [ord(c) for c in input.upper() if c.isupper()] |
|||
return $dest; |
|||
best_len = 0 |
|||
} |
|||
best_corr = -100.0 |
|||
# Assume that if there are less than 20 characters |
|||
?> |
|||
# per column, the key's too long to guess |
|||
</lang> |
|||
for i in xrange(2, len(cleaned) // 20): |
|||
{{out}} |
|||
pieces = [[] for _ in xrange(i)] |
|||
<pre>Text: Beware the Jabberwock, my son! The jaws that bite, the claws that catch! |
|||
for j, c in enumerate(cleaned): |
|||
key: VIGENERECIPHER |
|||
pieces[j % i].append(c) |
|||
Code: WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY |
|||
Back: BEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCH</pre> |
|||
# The correlation seems to increase for smaller |
|||
=={{header|PicoLisp}}== |
|||
# pieces/longer keys, so weigh against them a little |
|||
<lang PicoLisp>(de vigenereKey (Str) |
|||
corr = -0.5 * i + sum(correlation(p) for p in pieces) |
|||
(extract |
|||
'((C) |
|||
(when (>= "Z" (uppc C) "A") |
|||
(- (char (uppc C)) 65) ) ) |
|||
(chop Str) ) ) |
|||
if corr > best_corr: |
|||
(de vigenereEncrypt (Str Key) |
|||
best_len = i |
|||
(pack |
|||
best_corr = corr |
|||
'((C K) |
|||
(char (+ 65 (% (+ C K) 26))) ) |
|||
(vigenereKey Str) |
|||
(apply circ (vigenereKey Key)) ) ) ) |
|||
if best_len == 0: |
|||
(de vigenereDecrypt (Str Key) |
|||
return ("Text is too short to analyze", "") |
|||
(pack |
|||
(mapcar |
|||
'((C K) |
|||
(char (+ 65 (% (+ 26 (- C K)) 26))) ) |
|||
(vigenereKey Str) |
|||
(apply circ (vigenereKey Key)) ) ) )</lang> |
|||
Test: |
|||
<pre>: (vigenereEncrypt |
|||
"Beware the Jabberwock, my son! The jaws that bite, the claws that catch!" |
|||
"VIGENERECIPHER" ) |
|||
-> "WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY" |
|||
pieces = [[] for _ in xrange(best_len)] |
|||
: (vigenereDecrypt @ "VIGENERECIPHER") |
|||
for i, c in enumerate(cleaned): |
|||
-> "BEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCH"</pre> |
|||
pieces[i % best_len].append(c) |
|||
freqs = [frequency(p) for p in pieces] |
|||
=={{header|PL/I}}== |
|||
<lang PL/I> |
|||
cypher: procedure options (main); /* 21 September 2012 */ |
|||
declare t(26) character (26); |
|||
declare (i, j, k, L) fixed binary; |
|||
declare (original, encoded, coder) character (1000) varying initial (''); |
|||
declare cypher character (30) varying; |
|||
declare (co, ct, cc) character (1); |
|||
key = "" |
|||
for fr in freqs: |
|||
t(1) = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; |
|||
fr.sort(key=itemgetter(1), reverse=True) |
|||
do i = 2 to 26; |
|||
t(i) = substr(t(i-1), 2, 25) || substr(t(i-1), 1, 1); |
|||
end; |
|||
m = 0 |
|||
max_corr = 0.0 |
|||
for j in xrange(nchars): |
|||
put edit ('Message=', original) (a); |
|||
corr = 0.0 |
|||
original = uppercase(original); |
|||
c = ordA + j |
|||
for frc in fr: |
|||
d = (ord(frc[0]) - c + nchars) % nchars |
|||
corr += frc[1] * target_freqs[d] |
|||
if corr > max_corr: |
|||
/* Create the cypher text, same length as original, or longer. */ |
|||
m = j |
|||
coder = repeat(cypher, length(original)/length(cypher)); |
|||
max_corr = corr |
|||
key += chr(m + ordA) |
|||
/* Encode the original message, character by character. */ |
|||
/* Non-alphabetic characters are ignored. */ |
|||
L = 0; |
|||
do i = 1 to length(original); |
|||
co = substr(original, i, 1); |
|||
j = index(t(1), co); |
|||
if j = 0 then iterate; /* Ignore non-alphabetic character */ |
|||
L = L + 1; |
|||
ct = substr(coder, L, 1); |
|||
k = index(t(1), ct); |
|||
encoded = encoded || substr(t(j), k, 1); |
|||
end; |
|||
put skip data (encoded); |
|||
r = (chr((c - ord(key[i % best_len]) + nchars) % nchars + ordA) |
|||
/* DECODING. */ |
|||
for i, c in enumerate(cleaned)) |
|||
put skip list ('Decoded='); |
|||
return (key, "".join(r)) |
|||
do i = 1 to length(encoded); |
|||
cc = substr(coder, i, 1); |
|||
j = index(t(1), cc); |
|||
k = index(t(j), substr(encoded, i, 1)); |
|||
put edit (substr(t(1), k, 1) ) (a(1)); |
|||
end; |
|||
end cypher; |
|||
</lang> |
|||
{{out}} |
|||
<pre> |
|||
Message=Meet me on Tuesday evening at seven. |
|||
ENCODED='HMKBXEBPXPMYLLYRXIIQTOLTFGZZV'; |
|||
Decoded= MEETMEONTUESDAYEVENINGATSEVEN |
|||
</pre> |
|||
=={{header|PowerShell}}== |
|||
<lang Powershell># Author: D. Cudnohufsky |
|||
function Get-VigenereCipher |
|||
{ |
|||
Param |
|||
( |
|||
[Parameter(Mandatory=$true)] |
|||
[string] $Text, |
|||
[Parameter(Mandatory=$true)] |
|||
[string] $Key, |
|||
[switch] $Decode |
|||
) |
|||
begin |
|||
{ |
|||
$map = [char]'A'..[char]'Z' |
|||
} |
|||
process |
|||
{ |
|||
$Key = $Key -replace '[^a-zA-Z]','' |
|||
$Text = $Text -replace '[^a-zA-Z]','' |
|||
def main(): |
|||
$keyChars = $Key.toUpper().ToCharArray() |
|||
encoded = """ |
|||
$Chars = $Text.toUpper().ToCharArray() |
|||
MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH |
|||
VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD |
|||
function encode |
|||
ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS |
|||
{ |
|||
FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG |
|||
ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ |
|||
ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS |
|||
JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT |
|||
LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST |
|||
MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH |
|||
QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV |
|||
RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW |
|||
TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO |
|||
SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR |
|||
ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX |
|||
BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB |
|||
BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA |
|||
FWAML ZZRXJ EKAHV FASMU LVVUT TGK""" |
|||
english_frequences = [ |
|||
param |
|||
0.08167, 0.01492, 0.02782, 0.04253, 0.12702, 0.02228, 0.02015, |
|||
( |
|||
0.06094, 0.06966, 0.00153, 0.00772, 0.04025, 0.02406, 0.06749, |
|||
0.07507, 0.01929, 0.00095, 0.05987, 0.06327, 0.09056, 0.02758, |
|||
0.00978, 0.02360, 0.00150, 0.01974, 0.00074] |
|||
$Alpha = [char]'A'..[char]'Z' |
|||
) |
|||
(key, decoded) = vigenere_decrypt(english_frequences, encoded) |
|||
$charIndex = $Alpha.IndexOf([int]$Char) |
|||
print "Key:", key |
|||
$keyIndex = $Alpha.IndexOf([int]$keyChar) |
|||
print "\nText:", decoded |
|||
$NewIndex = ($charIndex + $KeyIndex) - $Alpha.Length |
|||
$Alpha[$NewIndex] |
|||
main()</syntaxhighlight> |
|||
} |
|||
function decode |
|||
{ |
|||
=={{header|Racket}}== |
|||
param |
|||
( |
|||
$Char, |
|||
$keyChar, |
|||
$Alpha = [char]'A'..[char]'Z' |
|||
) |
|||
=== Simple method === |
|||
$charIndex = $Alpha.IndexOf([int]$Char) |
|||
This is a simple method that just tries to find a key of any length that minimizes the difference from the expected English character distributions. |
|||
$keyIndex = $Alpha.IndexOf([int]$keyChar) |
|||
$int = $charIndex - $keyIndex |
|||
if ($int -lt 0) { $NewIndex = $int + $Alpha.Length } |
|||
else { $NewIndex = $int } |
|||
$Alpha[$NewIndex] |
|||
} |
|||
<syntaxhighlight lang="racket"> |
|||
while ( $keyChars.Length -lt $Chars.Length ) |
|||
#lang at-exp racket |
|||
{ |
|||
$keyChars = $keyChars + $keyChars |
|||
} |
|||
(define max-keylen 30) |
|||
for ( $i = 0; $i -lt $Chars.Length; $i++ ) |
|||
{ |
|||
(define text |
|||
if ( [int]$Chars[$i] -in $map -and [int]$keyChars[$i] -in $map ) |
|||
@~a{MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH |
|||
{ |
|||
VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD |
|||
if ($Decode) {$Chars[$i] = decode $Chars[$i] $keyChars[$i] $map} |
|||
ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS |
|||
else {$Chars[$i] = encode $Chars[$i] $keyChars[$i] $map} |
|||
FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG |
|||
ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ |
|||
ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS |
|||
JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT |
|||
LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST |
|||
MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH |
|||
QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV |
|||
RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW |
|||
TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO |
|||
SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR |
|||
ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX |
|||
BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB |
|||
BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA |
|||
FWAML ZZRXJ EKAHV FASMU LVVUT TGK}) |
|||
(define first-char (char->integer #\A)) |
|||
$Chars[$i] = [char]$Chars[$i] |
|||
(define chars# (- (char->integer #\Z) first-char -1)) |
|||
[string]$OutText += $Chars[$i] |
|||
} |
|||
(define freqs ; english letter frequencies from wikipedia |
|||
} |
|||
((compose1 list->vector (curry map (curryr / 100000.0))) |
|||
'(8167 1492 2782 4253 12702 2228 2015 6094 6966 153 772 4025 2406 |
|||
$OutText |
|||
6749 7507 1929 95 5987 6327 9056 2758 978 2360 150 1974 74))) |
|||
$OutText = $null |
|||
} |
|||
}</lang> |
|||
Usage examples: |
|||
<pre> |
|||
Encode: |
|||
PS C:\> Get-VigenereCipher 'We attack at dawn.' 'lemon' |
|||
HIMHGLGWOGOEIB |
|||
(define text* (for/vector ([c (regexp-replace* #px"\\s+" text "")]) |
|||
Decode: |
|||
(- (char->integer c) first-char))) |
|||
PS C:\> Get-VigenereCipher 'HIMHGLGWOGOEIB' 'lemon' -Decode |
|||
(define N (vector-length text*)) |
|||
WEATTACKATDAWN |
|||
</pre> |
|||
(define (col-guesses len) |
|||
=={{header|PureBasic}}== |
|||
(for/list ([ofs len]) |
|||
<lang PureBasic>Procedure prepString(text.s, Array letters(1)) |
|||
(define text (for/list ([i (in-range ofs N len)]) (vector-ref text* i))) |
|||
;convert characters to an ordinal (0-25) and remove non-alphabetic characters, |
|||
(define cN (length text)) |
|||
;returns dimension size of result array letters() |
|||
(define cfreqs (make-vector chars# 0)) |
|||
Protected *letter.Character, index |
|||
(for ([c (in-list text)]) |
|||
(vector-set! cfreqs c (add1 (vector-ref cfreqs c)))) |
|||
text = UCase(text) |
|||
(for ([i chars#]) (vector-set! cfreqs i (/ (vector-ref cfreqs i) cN))) |
|||
*letter = @text |
|||
(argmin car |
|||
While *letter\c |
|||
(for/list ([d chars#]) |
|||
Select *letter\c |
|||
(cons (for/sum ([i chars#]) |
|||
(expt (- (vector-ref freqs i) |
|||
letters(index) = *letter\c - 65 |
|||
(vector-ref cfreqs (modulo (+ i d) chars#))) |
|||
index + 1 |
|||
2)) |
|||
EndSelect |
|||
d))))) |
|||
Wend |
|||
If index > 0 |
|||
Redim letters(index - 1) |
|||
EndIf |
|||
ProcedureReturn index - 1 |
|||
EndProcedure |
|||
(define best-key |
|||
Procedure.s VC_encrypt(text.s, keyText.s, reverse = 0) |
|||
(cdr (argmin car |
|||
;if reverse <> 0 then reverse the key (decrypt) |
|||
(for/list ([len (range 1 (add1 max-keylen))]) |
|||
Protected *letter.Character |
|||
(define guesses (col-guesses len)) |
|||
Dim text(0) |
|||
(cons (/ (apply + (map car guesses)) len) (map cdr guesses)))))) |
|||
Dim keyText(0) |
|||
If prepString(text, text()) < 0 Or prepString(keyText, keyText()) < 0: ProcedureReturn: EndIf ;exit, nothing to work with |
|||
Protected i, keyLength = ArraySize(keyText()) |
|||
If reverse |
|||
For i = 0 To keyLength |
|||
keyText(i) = 26 - keyText(i) |
|||
Next |
|||
EndIf |
|||
Protected textLength = ArraySize(text()) ;zero-based length |
|||
Protected result.s = Space(textLength + 1), *resultLetter.Character |
|||
keyLength + 1 ;convert from zero-based to one-based count |
|||
*resultLetter = @result |
|||
For i = 0 To textLength |
|||
*resultLetter\c = ((text(i) + keyText(i % keyLength)) % 26) + 65 |
|||
*resultLetter + SizeOf(Character) |
|||
Next |
|||
ProcedureReturn result |
|||
EndProcedure |
|||
(printf "Best key found: ") |
|||
Procedure.s VC_decrypt(cypherText.s, keyText.s) |
|||
(for ([c best-key]) (display (integer->char (+ c first-char)))) |
|||
ProcedureReturn VC_encrypt(cypherText, keyText.s, 1) |
|||
(newline) |
|||
EndProcedure |
|||
(printf "Decoded text:\n") |
|||
If OpenConsole() |
|||
(define decode-num |
|||
Define VignereCipher.s, plainText.s, encryptedText.s, decryptedText.s |
|||
(let ([cur '()]) |
|||
(λ(n) (when (null? cur) (set! cur best-key)) |
|||
VignereCipher.s = "VIGNERECIPHER" |
|||
(begin0 (modulo (- n (car cur)) chars#) (set! cur (cdr cur)))))) |
|||
plainText = "The quick brown fox jumped over the lazy dogs.": PrintN(RSet("Plain text = ", 17) + #DQUOTE$ + plainText + #DQUOTE$) |
|||
(for ([c text]) |
|||
encryptedText = VC_encrypt(plainText, VignereCipher): PrintN(RSet("Encrypted text = ", 17) + #DQUOTE$ + encryptedText + #DQUOTE$) |
|||
(define n (- (char->integer c) first-char)) |
|||
decryptedText = VC_decrypt(encryptedText, VignereCipher): PrintN(RSet("Decrypted text = ", 17) + #DQUOTE$ + decryptedText + #DQUOTE$) |
|||
(if (not (< -1 n chars#)) (display c) |
|||
(display (integer->char (+ first-char (decode-num n)))))) |
|||
Print(#CRLF$ + #CRLF$ + "Press ENTER to exit"): Input() |
|||
(newline) |
|||
CloseConsole() |
|||
</syntaxhighlight> |
|||
EndIf</lang> |
|||
{{out}} |
|||
<pre> Plain text = "The quick brown fox jumped over the lazy dogs." |
|||
Encrypted text = "OPKDYZGMJGVAEAWDWYDTGLDCIIOPKYEQCFWVZ" |
|||
Decrypted text = "THEQUICKBROWNFOXJUMPEDOVERTHELAZYDOGS"</pre> |
|||
Output: |
|||
=={{header|Python}}== |
|||
<pre> |
|||
{{Works with|Python|3}} |
|||
Best key found: THECHESHIRECAT |
|||
{{trans|Haskell}} |
|||
Decoded text: |
|||
<lang python>'''Vigenere encryption and decryption''' |
|||
THISW ASTHE POEMT HATAL ICERE ADJAB BERWO CKYTW ASBRI LLIGA |
|||
... |
|||
</pre> |
|||
=== An attempted more complete implementation === |
|||
from itertools import starmap, cycle |
|||
This is an attempt at following the [http://en.wikipedia.org/wiki/Index_of_coincidence#Example Wikipedia] description. However, it performs just as well as the simple version. Most likely because I know almost nothing about cryptography... |
|||
<syntaxhighlight lang="racket"> |
|||
#lang at-exp racket |
|||
(define max-keylen 30) |
|||
def encrypt(message, key): |
|||
'''Vigenere encryption of message using key.''' |
|||
(define text |
|||
# Converted to uppercase. |
|||
@~a{MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH |
|||
# Non-alpha characters stripped out. |
|||
VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD |
|||
message = filter(str.isalpha, message.upper()) |
|||
ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS |
|||
FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG |
|||
ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ |
|||
ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS |
|||
JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT |
|||
LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST |
|||
MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH |
|||
QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV |
|||
RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW |
|||
TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO |
|||
SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR |
|||
ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX |
|||
BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB |
|||
BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA |
|||
FWAML ZZRXJ EKAHV FASMU LVVUT TGK}) |
|||
(define first-char (char->integer #\A)) |
|||
def enc(c, k): |
|||
(define chars# (- (char->integer #\Z) first-char -1)) |
|||
'''Single letter encryption.''' |
|||
(define freqs ; english letter frequencies from wikipedia |
|||
return chr(((ord(k) + ord(c) - 2 * ord('A')) % 26) + ord('A')) |
|||
((compose1 list->vector (curry map (curryr / 100000.0))) |
|||
'(8167 1492 2782 4253 12702 2228 2015 6094 6966 153 772 4025 2406 |
|||
6749 7507 1929 95 5987 6327 9056 2758 978 2360 150 1974 74))) |
|||
(define (n*n-1 n) (* n (sub1 n))) |
|||
return ''.join(starmap(enc, zip(message, cycle(key)))) |
|||
(define text* (for/vector ([c (regexp-replace* #px"\\s+" text "")]) |
|||
(- (char->integer c) first-char))) |
|||
(define N (vector-length text*)) |
|||
(define (get-col-length+freqs width offset) |
|||
(define text (for/list ([i (in-range offset N width)]) (vector-ref text* i))) |
|||
(define cN (length text)) |
|||
(define freqs (make-vector chars# 0)) |
|||
(for ([c (in-list text)]) (vector-set! freqs c (add1 (vector-ref freqs c)))) |
|||
(values cN freqs)) |
|||
(define expected-IC (* chars# (for*/sum ([x freqs]) (* x x)))) |
|||
def decrypt(message, key): |
|||
'''Vigenere decryption of message using key.''' |
|||
;; maps key lengths to average index of coincidence |
|||
def dec(c, k): |
|||
(define keylen->ICs |
|||
'''Single letter decryption.''' |
|||
(for/vector ([len (in-range 1 (add1 (* max-keylen 2)))]) |
|||
(for/sum ([ofs len]) |
|||
(define-values [cN cfreqs] (get-col-length+freqs len ofs)) |
|||
(/ (for/sum ([i chars#]) (n*n-1 (vector-ref cfreqs i))) |
|||
(/ (n*n-1 cN) chars#) len 1.0)))) |
|||
;; given a key length find the key that minimizes errors from alphabet freqs, |
|||
return chr(((ord(c) - ord(k) - 2 * ord('A')) % 26) + ord('A')) |
|||
;; return (cons average-error key) |
|||
(define (guess-key len) |
|||
(define guesses |
|||
(for/list ([ofs len]) |
|||
(define-values [cN cfreqs] (get-col-length+freqs len ofs)) |
|||
(for ([i chars#]) (vector-set! cfreqs i (/ (vector-ref cfreqs i) cN))) |
|||
(argmin car |
|||
(for/list ([d chars#]) |
|||
(cons (for/sum ([i chars#]) |
|||
(expt (- (vector-ref freqs i) |
|||
(vector-ref cfreqs (modulo (+ i d) chars#))) |
|||
2)) |
|||
d))))) |
|||
(cons (/ (apply + (map car guesses)) len) (map cdr guesses))) |
|||
;; look for a key length that minimizes error from expected-IC, with some |
|||
return ''.join(starmap(dec, zip(message, cycle(key)))) |
|||
;; stupid consideration of multiples of the length (which should also have low |
|||
;; errors), for each one guess a key, then find the one that minimizes both (in |
|||
;; a way that looks like it works, but undoubtedly is wrong in all kinds of |
|||
;; ways) and return the winner key |
|||
(define best-key |
|||
((compose1 cdr (curry argmin car)) |
|||
(for/list ([i (* max-keylen 2)]) |
|||
;; get the error from the expected-IC for the length and its multiples, |
|||
;; with decreasing weights for the multiples |
|||
(define with-multiples |
|||
(for/list ([j (in-range i (* max-keylen 2) (add1 i))] [div N]) |
|||
(cons (/ (abs (- (vector-ref keylen->ICs j) expected-IC)) expected-IC) |
|||
(/ (add1 div))))) |
|||
(define total (/ (for/sum ([x with-multiples]) (* (car x) (cdr x))) |
|||
(for/sum ([x with-multiples]) (cdr x)))) |
|||
(define guess (guess-key (add1 i))) |
|||
(define guess*total (* total (car guess) (car guess))) |
|||
;; (printf "~a~a: ~a ~s\n" (if (< i 9) " " "") (add1 i) |
|||
;; (list total (car guess) guess*total) (cdr guess)) |
|||
(cons guess*total (cdr guess))))) |
|||
(printf "Best key found: ") |
|||
(for ([c best-key]) (display (integer->char (+ c first-char)))) |
|||
(newline) |
|||
(printf "Decoded text:\n") |
|||
def main(): |
|||
(define decode-num |
|||
'''Demonstration''' |
|||
(let ([cur '()]) |
|||
(λ(n) (when (null? cur) (set! cur best-key)) |
|||
text = 'Beware the Jabberwock, my son! The jaws that bite, ' + ( |
|||
(begin0 (modulo (- n (car cur)) chars#) (set! cur (cdr cur)))))) |
|||
'the claws that catch!' |
|||
(for ([c text]) |
|||
) |
|||
(define n (- (char->integer c) first-char)) |
|||
key = 'VIGENERECIPHER' |
|||
(if (not (< -1 n chars#)) (display c) |
|||
(display (integer->char (+ first-char (decode-num n)))))) |
|||
encr = encrypt(text, key) |
|||
(newline) |
|||
decr = decrypt(encr, key) |
|||
</syntaxhighlight> |
|||
print(text) |
|||
print(encr) |
|||
print(decr) |
|||
if __name__ == '__main__': |
|||
main() |
|||
</lang> |
|||
{{out}} |
|||
<pre>Beware the Jabberwock, my son! The jaws that bite, the claws that catch! |
|||
WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY |
|||
BEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCH</pre> |
|||
=={{header|R}}== |
|||
<lang r>mod1 = function(v, n) |
|||
# mod1(1:20, 6) => 1 2 3 4 5 6 1 2 3 4 5 6 1 2 3 4 5 6 1 2 |
|||
((v - 1) %% n) + 1 |
|||
str2ints = function(s) |
|||
as.integer(Filter(Negate(is.na), |
|||
factor(levels = LETTERS, strsplit(toupper(s), "")[[1]]))) |
|||
vigen = function(input, key, decrypt = F) |
|||
{input = str2ints(input) |
|||
key = rep(str2ints(key), len = length(input)) - 1 |
|||
paste(collapse = "", LETTERS[ |
|||
mod1(input + (if (decrypt) -1 else 1)*key, length(LETTERS))])} |
|||
message(vigen("Beware the Jabberwock, my son! The jaws that bite, the claws that catch!", "vigenerecipher")) |
|||
# WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY |
|||
message(vigen("WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY", "vigenerecipher", decrypt = T)) |
|||
# BEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCH</lang> |
|||
=={{header|Racket}}== |
|||
<lang racket> |
|||
#lang racket |
|||
(define chr integer->char) |
|||
(define ord char->integer) |
|||
(define (encrypt msg key) |
|||
(define cleaned |
|||
(list->string |
|||
(for/list ([c (string-upcase msg)] |
|||
#:when (char-alphabetic? c)) c))) |
|||
(list->string |
|||
(for/list ([c cleaned] [k (in-cycle key)]) |
|||
(chr (+ (modulo (+ (ord c) (ord k)) 26) (ord #\A)))))) |
|||
(define (decrypt msg key) |
|||
(list->string |
|||
(for/list ([c msg] [k (in-cycle key)]) |
|||
(chr (+ (modulo (- (ord c) (ord k)) 26) (ord #\A)))))) |
|||
(decrypt (encrypt "Beware the Jabberwock, my son! The jaws that bite, the claws that catch!" |
|||
"VIGENERECIPHER") |
|||
"VIGENERECIPHER") |
|||
</lang> |
|||
{{out}} |
|||
<lang racket> |
|||
"BEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCH" |
|||
</lang> |
|||
=={{header|Raku}}== |
=={{header|Raku}}== |
||
(formerly Perl 6) |
(formerly Perl 6) |
||
{{trans|Perl}} |
|||
{{Works with|rakudo|2015-11-14}} |
|||
<syntaxhighlight lang="raku" line># from Wikipedia |
|||
<lang perl6>sub s2v ($s) { $s.uc.comb(/ <[ A..Z ]> /)».ord »-» 65 } |
|||
constant %English-letter-freq = ( |
|||
sub v2s (@v) { (@v »%» 26 »+» 65)».chr.join } |
|||
E => 12.70, L => 4.03, Y => 1.97, P => 1.93, T => 9.06, A => 8.17, O => 7.51, I => 6.97, N => 6.75, |
|||
S => 6.33, H => 6.09, R => 5.99, D => 4.25, C => 2.78, U => 2.76, M => 2.41, W => 2.36, F => 2.23, |
|||
G => 2.02, B => 1.29, V => 0.98, K => 0.77, J => 0.15, X => 0.15, Q => 0.10, Z => 0.07 |
|||
); |
|||
constant @alphabet = %English-letter-freq.keys.sort; |
|||
constant max_key_lengths = 5; # number of keylengths to try |
|||
sub myguess ($text) { |
|||
sub blacken ($red, $key) { v2s(s2v($red) »+» s2v($key)) } |
|||
my ($seqtext, @spacing, @factors, $pos, %freq, %Keys); |
|||
sub redden ($blk, $key) { v2s(s2v($blk) »-» s2v($key)) } |
|||
# Kasiski examination |
|||
my $red = "Beware the Jabberwock, my son! The jaws that bite, the claws that catch!"; |
|||
$seqtext = $text; |
|||
my $key = "Vigenere Cipher!!!"; |
|||
while ($seqtext ~~ /$<sequence>=[...].*$<sequence>/) { |
|||
$seqtext = substr($seqtext, 1+index($seqtext, $<sequence>)); |
|||
push @spacing, 1 + index($seqtext, $<sequence>); |
|||
} |
|||
for @spacing -> $j { |
|||
%freq{$_}++ for grep { $j %% $_ }, 2..$j; |
|||
} |
|||
# discard very short keys, and test only the most likely remaining key lengths |
|||
say $red; |
|||
(%freq.keys.grep(* > 3).sort({%freq{$_}}).tail(max_key_lengths)).race(:1batch).map: -> $keylen { |
|||
say my $black = blacken($red, $key); |
|||
my $key-guess = ''; |
|||
say redden($black, $key);</lang> |
|||
loop (my $i = 0; $i < $keylen; $i++) { |
|||
{{out}} |
|||
my ($mykey, %chi-square, $best-guess); |
|||
<pre>Beware the Jabberwock, my son! The jaws that bite, the claws that catch! |
|||
loop (my $j = 0; $j < $text.chars; $j += $keylen) { |
|||
WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY |
|||
$mykey ~= substr($text, ($j+$i) % $text.chars, 1); |
|||
BEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCH</pre> |
|||
} |
|||
This is a natural job for hyperoperators, which can vectorize any operator. |
|||
For infix operators the pointy end indicates which side to "dwim", repeating |
|||
elements on that side until the other side runs out. In particular, repeating |
|||
the key naturally falls out of this cyclic dwimmery, as does repeating the various constants to be applied with any of several operations to every element of the list. Factoring out the canonicalization and decanonicalization lets us see quite clearly that the only difference between encryption and decryptions is the sign of the vector addition/subtraction. Since hyperops are inherently parallelizable, this algorithm might run well in your GPU. |
|||
for @alphabet -> $subkey { |
|||
=={{header|Red}}== |
|||
my $decrypted = mycrypt($mykey, $subkey); |
|||
note: this program is much longer than it needed to be - because i couldn't resist |
|||
my $length = $decrypted.chars; |
|||
to add some more features to make it actually "useful" :-) So not only can u encrypt any character (because the crypted message will be base 64 encoded), but it also includes a Gui. |
|||
for @alphabet -> $char { |
|||
the Gui window has buttons to access the clipboard too - so u can get the original text from clipboard and put the crypted message back again. To execute it,simply download the latest red.exe (about 1,1 MB size! ) from red-lang.org. This program can also be compiled to an .exe (+ red runtime.dll ) by simply execute <pre>red.exe -c vign1.red</pre> or <pre>red.exe -r vign1.red</pre> which creates a single .exe file whithout the need for any .dll . should be working on windows , linux ( under wine ) and mac OS. |
|||
my $expected = %English-letter-freq{$char} * $length / 100; |
|||
<lang Red>Red [needs: 'view] |
|||
my $observed = $decrypted.comb.grep(* eq $char).elems; |
|||
%chi-square{$subkey} += ($observed - $expected)² / $expected if $observed; |
|||
} |
|||
} |
|||
%Keys{$keylen}{'score'} = %chi-square{@alphabet[0]}; |
|||
for %chi-square.keys.sort -> $sk { |
|||
if (%chi-square{$sk} <= %Keys{$keylen}{'score'}) { |
|||
$best-guess = $sk; |
|||
%Keys{$keylen}{'score'} = %chi-square{$sk}; |
|||
} |
|||
} |
|||
$key-guess ~= $best-guess; |
|||
} |
|||
%Keys{$keylen}{'key'} = $key-guess; |
|||
} |
|||
%Keys.keys.sort({ %Keys{$_}{'score'} }).map:{ %Keys{$_}{'key'} }; |
|||
} |
|||
sub mycrypt ($text, $key) { |
|||
CRLF: copy "^M^/" ;; constant for 0D 0A line feed |
|||
constant %values-numbers = @alphabet Z=> ^@alphabet; |
|||
;;------------------------------------ |
|||
constant %values-letters = %values-numbers.invert; |
|||
crypt: func ["function to en- or decrypt message from textarea tx1" |
|||
/decrypt "decrypting switch/refinement" ][ |
|||
;;------------------------------------ |
|||
my ($new-text); |
|||
;; when decrypting we have to remove the superflous newlines |
|||
my $keylen = $key.chars; |
|||
;; and undo the base64 encoding first ... |
|||
loop (my $i = 0; $i < $text.chars; $i++) { |
|||
txt: either decrypt [ ;; message to en- or decrypt |
|||
my $val = -1 * %values-numbers{substr( $key, $i%$keylen, 1)} # negative shift for decode |
|||
s: copy tx1/text |
|||
+ %values-numbers{substr($text, $i, 1)}; |
|||
;; newline could be single 0a byte or crlf sequence when copied from clipboard... |
|||
$new-text ~= %values-letters{ $val % @alphabet }; |
|||
debase replace/all s either find s CRLF [CRLF ] [ newline ] "" |
|||
} |
|||
] [ |
|||
return $new-text; |
|||
} |
|||
] |
|||
my $cipher-text = .uc.trans(@alphabet => '', :c) given q:to/EOD/; |
|||
txt: to-binary txt ;; handle message as binary |
|||
MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH |
|||
key: to-binary key1/text ;; handle key also as binary |
|||
VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD |
|||
ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS |
|||
FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG |
|||
ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ |
|||
ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS |
|||
JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT |
|||
LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST |
|||
MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH |
|||
QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV |
|||
RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW |
|||
TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO |
|||
SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR |
|||
ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX |
|||
BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB |
|||
BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA |
|||
FWAML ZZRXJ EKAHV FASMU LVVUT TGK |
|||
EOD |
|||
for myguess($cipher-text) -> $key { |
|||
bin: copy either decrypt [ "" ][ #{} ] ;; buffer for output |
|||
say "Key $key\n" ~ |
|||
"Key length {$key.chars}\n" ~ |
|||
"Plaintext {substr(mycrypt($cipher-text, $key), 0, 80)}...\n"; |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre>Key THECHESHIRECAT |
|||
Key length 14 |
|||
Plaintext THISWASTHEPOEMTHATALICEREADJABBERWOCKYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIMB... |
|||
Key THECHESCIRECATTHECHESHIRECAT |
|||
code: copy #{} ;; temp field to collect utf8 bytes when decrypting |
|||
Key length 28 |
|||
Plaintext THISWASYHEPOEMTHATALICEREADJABBERWOHKYTWASBRILLIGANDTHESLITHYTOAESDIDGYREANDGIMB... |
|||
Key TJGGAHET |
|||
;; loop over length of binary! message ... |
|||
Key length 8 |
|||
Plaintext TFGODXGHWMNKEYIVLMBJACIPPTXWTBBNFRADSITFHCOSMGOTFYPOXCASLGRDFQCJTABEDSNFPTOBYIQZ... |
|||
k: to-integer key/(1 + modulo pos length? key) ;; get corresponding key byte |
|||
c: to-integer txt/:pos ;; get integer value from message byte at position pos |
|||
either decrypt [ ;; decrypting ? |
|||
c: modulo ( 256 + c - k ) 256 ;; compute original byte value |
|||
case [ |
|||
;; byte starting with 11.... ( >= 192 dec ) is utf8 startbyte |
|||
;; byte starting with 10... ( >= 128 dec) is utf8 follow up byte , below is single ascii byte |
|||
( c >= 192 ) or ( c < 128 ) [ ;; starting utf8 sequence byte or below 128 normal ascii ? |
|||
;; append last code to buffer, maybe normal ascii or utf8 sequence... |
|||
if not empty? code [ append bin to-char code ] ;; save previous code first |
|||
code: append copy #{} c ;; start new code |
|||
] |
|||
true [ append code c ] ;; otherwise utf8 follow up byte, append to startbyte |
|||
] |
|||
][ |
|||
append bin modulo ( c + k ) 256 ;; encrypting , simply collect binary bytes |
|||
] |
|||
] ;; close repeat loop |
|||
Key THECSAS |
|||
either decrypt [ ;; collect utf-8 characters |
|||
Key length 7 |
|||
append bin to-char code ;; append last code |
|||
Plaintext THISLESHIRRYENTHATPPIQFEGKDKABBEGAOQLLVGATBRILAMGOOQVRETLITHNXOJFFFSDHYREACHGWNO... |
|||
tx2/text: to-string bin ;; create valid utf8 string when decrypting |
|||
][ ;; base64 encoding of crypted binary to get readable text string... |
|||
s: enbase copy bin ;; base 64 is default |
|||
while [40 < length? s ] [ ;; insert newlines for better "readability" |
|||
s: skip s either head? s [40][41] ;; ... every 40 characters |
|||
insert s newline |
|||
] |
|||
tx2/text: head s ;; reset s pointing to head again |
|||
] |
|||
] |
|||
;---------------------------------------------------------- |
|||
; start of program |
|||
;---------------------------------------------------------- |
|||
view layout [title "vigenere cyphre" ;Define nice GUI :- ) |
|||
;---------------------------------------------------------- |
|||
backdrop silver ;; define window background colour |
|||
text "message:" pad 99x1 button "get-clip" [tx1/text: read-clipboard] |
|||
;; code in brackets will be executed, when button is clicked: |
|||
button "clear" [tx1/text: copy "" ] return |
|||
tx1: area 330x80 "" return |
|||
text 25x20 "Key:" key1: field 290x20 "secretkey" return |
|||
button "crypt" [crypt ] button "decrypt" [crypt/decrypt ] |
|||
button "swap" [tx1/text: copy tx2/text tx2/text: copy "" ] return |
|||
text "de-/encrypted message:" pad 50x1 button "copy clip" [ write-clipboard tx2/text] |
|||
button "clear" [tx2/text: copy "" ] return |
|||
tx2: area 330x100 return |
|||
pad 270x1 button "Quit " [quit] |
|||
] |
|||
</lang> |
|||
{{output}} |
|||
the message <pre>Beware the Jabberwock, my son! The jaws that bite, the claws that catch!</pre> with key "VIGENERECIPHER" translates to <pre>i6y8r7e3ZbextWiPs7irrLfFtLWwb2m9wWXFxbdo |
|||
ZaKtt2Wtqse7Zca+qrtlsK7Gqm9pxLCqcrm1qLzBZcatpL1wq6bGubFo</pre> |
|||
decrypting returns the original message <pre>Beware the Jabberwock, my son! The jaws that bite, the claws that catch!</pre> |
|||
Key THEC |
|||
=={{header|REXX}}== |
|||
Key length 4 |
|||
===uppercase text only=== |
|||
Plaintext THISKXGYWOPOLYIMLODNHCIGPVZAABBEFTCHZITWHEQWTGOKFARSECAJLITHMQCATCDIKSNWPVQFFIQQ...</pre> |
|||
<lang rexx>/*REXX program encrypts (and displays) uppercased text using the Vigenère cypher.*/ |
|||
@.1 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' |
|||
L=length(@.1) |
|||
do j=2 to L; jm=j-1; q=@.jm |
|||
@.j=substr(q, 2, L - 1)left(q, 1) |
|||
end /*j*/ |
|||
cypher = space('WHOOP DE DOO NO BIG DEAL HERE OR THERE', 0) |
|||
oMsg = 'People solve problems by trial and error; judgement helps pick the trial.' |
|||
oMsgU = oMsg; upper oMsgU |
|||
cypher_= copies(cypher, length(oMsg) % length(cypher) ) |
|||
say ' original text =' oMsg |
|||
xMsg= Ncypher(oMsgU); say ' cyphered text =' xMsg |
|||
bMsg= Dcypher(xMsg) ; say 're-cyphered text =' bMsg |
|||
exit |
|||
/*──────────────────────────────────────────────────────────────────────────────────────*/ |
|||
Ncypher: parse arg x; nMsg=; #=1 /*unsupported char? ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓*/ |
|||
do i=1 for length(x); j=pos(substr(x,i,1), @.1); if j==0 then iterate |
|||
nMsg=nMsg || substr(@.j, pos( substr( cypher_, #, 1), @.1), 1); #=#+1 |
|||
end /*j*/ |
|||
return nMsg |
|||
/*──────────────────────────────────────────────────────────────────────────────────────*/ |
|||
Dcypher: parse arg x; dMsg= |
|||
do i=1 for length(x); j=pos(substr(cypher_, i, 1), @.1) |
|||
dMsg=dMsg || substr(@.1, pos( substr(x, i, 1), @.j), 1 ) |
|||
end /*j*/ |
|||
return dMsg</lang> |
|||
{{out|output|text= when using the default internal fields:}} |
|||
<pre> |
|||
original text = People solve problems by trial and error; judgement helps pick the trial. |
|||
cyphered text = LLCDAHWRZJRDSWHOIMDICKVWREHRUINYCFYXHJSARBUPKOTSAPGBXVVMYMRP |
|||
re-cyphered text = PEOPLESOLVEPROBLEMSBYTRIALANDERRORJUDGEMENTHELPSPICKTHETRIAL |
|||
</pre> |
|||
===supports most characters=== |
|||
This version supports all characters on the IBM Model M keyboard, including blanks, but any other |
|||
<br>characters can be added as long as they're viewable. |
|||
Additional characters can be added by simply appending them to the <big>'''@.1'''</big> variable. |
|||
<lang rexx>/*REXX program encrypts (and displays) most text using the Vigenère cypher. */ |
|||
@abc= 'abcdefghijklmnopqrstuvwxyz'; @abcU=@abc; upper @abcU |
|||
@.1 = @abcU || @abc'0123456789~`!@#$%^&*()_-+={}|[]\:;<>?,./" ''' |
|||
L=length(@.1) |
|||
do j=2 to length(@.1); jm=j - 1; q=@.jm |
|||
@.j=substr(q, 2, L - 1)left(q, 1) |
|||
end /*j*/ |
|||
cypher = space('WHOOP DE DOO NO BIG DEAL HERE OR THERE', 0) |
|||
oMsg = 'Making things easy is just knowing the shortcuts. --- Gerard J. Schildberger' |
|||
cypher_= copies(cypher, length(oMsg) % length(cypher) ) |
|||
say ' original text =' oMsg |
|||
xMsg= Ncypher(oMsg); say ' cyphered text =' xMsg |
|||
bMsg= Dcypher(xMsg); say 're-cyphered text =' bMsg |
|||
exit |
|||
/*──────────────────────────────────────────────────────────────────────────────────────*/ |
|||
Ncypher: parse arg x; nMsg=; #=1 /*unsupported char? ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓*/ |
|||
do i=1 for length(x); j=pos(substr(x,i,1), @.1); if j==0 then iterate |
|||
nMsg=nMsg || substr(@.j, pos( substr( cypher_, #, 1), @.1), 1); #=# + 1 |
|||
end /*j*/ |
|||
return nMsg |
|||
/*──────────────────────────────────────────────────────────────────────────────────────*/ |
|||
Dcypher: parse arg x; dMsg= |
|||
do i=1 for length(x); j=pos(substr(cypher_, i, 1), @.1) |
|||
dMsg=dMsg || substr(@.1, pos( substr(x, i, 1), @.j), 1 ) |
|||
end /*j*/ |
|||
return dMsg</lang> |
|||
{{out|output|text= when using the default internal fields:}} |
|||
<pre> |
|||
original text = Making things easy is just knowing the shortcuts. --- Gerard J. Schildberger |
|||
cyphered text = ihyw2jCwvw0utGkdwyJpwPn89!Fo4s&p1uNwlhM6u2s1ixxsGF}"}MXxye8h/H?/QafgjbZcpecp |
|||
re-cyphered text = Making things easy is just knowing the shortcuts. --- Gerard J. Schildberger |
|||
</pre> |
|||
=={{header|Ring}}== |
|||
<lang ring> |
|||
# Project : Vigenère cipher |
|||
key = "LEMON" |
|||
plaintext = "ATTACK AT DAWN" |
|||
ciphertext = encrypt(plaintext, key) |
|||
see "key = "+ key + nl |
|||
see "plaintext = " + plaintext + nl |
|||
see "ciphertext = " + ciphertext + nl |
|||
see "decrypted = " + decrypt(ciphertext, key) + nl |
|||
func encrypt(plain, key) |
|||
o = "" |
|||
k = 0 |
|||
plain = fnupper(plain) |
|||
key = fnupper(key) |
|||
for i = 1 to len(plain) |
|||
n = ascii(plain[i]) |
|||
if n >= 65 and n <= 90 |
|||
o = o + char(65 + (n + ascii(key[k+1])) % 26) |
|||
k = (k + 1) % len(key) |
|||
ok |
|||
next |
|||
return o |
|||
func decrypt(cipher, key) |
|||
o = "" |
|||
k = 0 |
|||
cipher = fnupper(cipher) |
|||
key = fnupper(key) |
|||
for i = 1 to len(cipher) |
|||
n = ascii(cipher[i]) |
|||
o = o + char(65 + (n + 26 - ascii(key[k+1])) % 26) |
|||
k = (k + 1) % len(key) |
|||
next |
|||
return o |
|||
func fnupper(a) |
|||
for aa = 1 to len(a) |
|||
c = ascii(a[aa]) |
|||
if c >= 97 and c <= 122 |
|||
a[aa] = char(c-32) |
|||
ok |
|||
next |
|||
return a |
|||
</lang> |
|||
Output: |
|||
<pre> |
|||
key = LEMON |
|||
plaintext = ATTACK AT DAWN |
|||
ciphertext = LXFOPVEFRNHR |
|||
decrypted = ATTACKATDAWN |
|||
</pre> |
|||
=={{header|Ruby}}== |
|||
<lang Ruby>module VigenereCipher |
|||
BASE = 'A'.ord |
|||
SIZE = 'Z'.ord - BASE + 1 |
|||
def encrypt(text, key) |
|||
crypt(text, key, :+) |
|||
end |
|||
def decrypt(text, key) |
|||
crypt(text, key, :-) |
|||
end |
|||
def crypt(text, key, dir) |
|||
text = text.upcase.gsub(/[^A-Z]/, '') |
|||
key_iterator = key.upcase.gsub(/[^A-Z]/, '').chars.map{|c| c.ord - BASE}.cycle |
|||
text.each_char.inject('') do |ciphertext, char| |
|||
offset = key_iterator.next |
|||
ciphertext << ((char.ord - BASE).send(dir, offset) % SIZE + BASE).chr |
|||
end |
|||
end |
|||
end</lang> |
|||
Demonstration: |
|||
<lang Ruby>include VigenereCipher |
|||
plaintext = 'Beware the Jabberwock, my son! The jaws that bite, the claws that catch!' |
|||
key = 'Vigenere cipher' |
|||
ciphertext = VigenereCipher.encrypt(plaintext, key) |
|||
recovered = VigenereCipher.decrypt(ciphertext, key) |
|||
puts "Original: #{plaintext}" |
|||
puts "Encrypted: #{ciphertext}" |
|||
puts "Decrypted: #{recovered}"</lang> |
|||
{{out}} |
|||
<pre> |
|||
Original: Beware the Jabberwock, my son! The jaws that bite, the claws that catch! |
|||
Encrypted: WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY |
|||
Decrypted: BEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCH |
|||
</pre> |
|||
=={{header|Rust}}== |
=={{header|Rust}}== |
||
{{trans|Kotlin}} |
|||
<lang rust>use std::ascii::AsciiExt; |
|||
Note that the character to/from byte ('''u8''') conversions work here only because the key and cryptogram are composed of ASCII characters only. Indeed, Rust's '''char''' type is a ''Unicode scalar value'', how they are represented is well summarized in the Rust book's [https://doc.rust-lang.org/std/primitive.char.html subchapter on strings]. |
|||
<syntaxhighlight lang="rust"> |
|||
use std::iter::FromIterator; |
|||
const CRYPTOGRAM: &str = "MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH |
|||
static A: u8 = 'A' as u8; |
|||
VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD |
|||
ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS |
|||
FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG |
|||
ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ |
|||
ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS |
|||
JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT |
|||
LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST |
|||
MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH |
|||
QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV |
|||
RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW |
|||
TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO |
|||
SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR |
|||
ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX |
|||
BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB |
|||
BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA |
|||
FWAML ZZRXJ EKAHV FASMU LVVUT TGK"; |
|||
const FREQUENCIES: [f32; 26] = [ |
|||
fn uppercase_and_filter(input: &str) -> Vec<u8> { |
|||
0.08167, 0.01492, 0.02202, 0.04253, 0.12702, 0.02228, 0.02015, 0.06094, 0.06966, 0.00153, |
|||
let alphabet = b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; |
|||
0.01292, 0.04025, 0.02406, 0.06749, 0.07507, 0.01929, 0.00095, 0.05987, 0.06327, 0.09356, |
|||
let mut result = Vec::new(); |
|||
0.02758, 0.00978, 0.02560, 0.00150, 0.01994, 0.00077, |
|||
]; |
|||
fn best_match(a: &[f32]) -> u8 { |
|||
for c in input.chars() { |
|||
let sum: f32 = a.iter().sum(); |
|||
// Ignore anything that is not in our short list of chars. We can then safely cast to u8. |
|||
let mut best_fit = std::f32::MAX; |
|||
if alphabet.iter().any(|&x| x as char == c) { |
|||
let mut best_rotate = 0; |
|||
result.push(c.to_ascii_uppercase() as u8); |
|||
for rotate in 0..=25 { |
|||
let mut fit = 0.; |
|||
for i in 0..=25 { |
|||
let char_freq = FREQUENCIES[i]; |
|||
let idx = (i + rotate as usize) % 26 as usize; |
|||
let d = a[idx] / sum - char_freq; |
|||
fit += d * d / char_freq; |
|||
} |
|||
if fit < best_fit { |
|||
best_fit = fit; |
|||
best_rotate = rotate; |
|||
} |
} |
||
} |
} |
||
best_rotate |
|||
} |
} |
||
fn |
fn freq_every_nth(msg: &[u8], key: &mut [char]) -> f32 { |
||
let len = msg.len(); |
|||
let |
let interval = key.len(); |
||
let |
let mut accu = [0.; 26]; |
||
for j in 0..interval { |
|||
let mut |
let mut out = [0.; 26]; |
||
for i in (j..len).step_by(interval) { |
|||
let idx = msg[i] as usize; |
|||
for (i, c) in text_bytes.iter().enumerate() { |
|||
out[idx] += 1.; |
|||
} |
|||
(c + key_bytes[i % key_bytes.len()] - 2 * A) % 26 + A |
|||
let rot = best_match(&out); |
|||
key[j] = char::from(rot + b'A'); |
|||
for i in 0..=25 { |
|||
let idx: usize = (i + rot as usize) % 26; |
|||
accu[i] += out[idx]; |
|||
} |
|||
} |
} |
||
let sum: f32 = accu.iter().sum(); |
|||
let mut ret = 0.; |
|||
for i in 0..=25 { |
|||
let char_freq = FREQUENCIES[i]; |
|||
let d = accu[i] / sum - char_freq; |
|||
ret += d * d / char_freq; |
|||
} |
|||
ret |
|||
} |
|||
fn decrypt(text: &str, key: &str) -> String { |
|||
String::from_utf8(result_bytes).unwrap() |
|||
let key_chars_cycle = key.as_bytes().iter().map(|b| *b as i32).cycle(); |
|||
let is_ascii_uppercase = |c: &u8| (b'A'..=b'Z').contains(c); |
|||
text.as_bytes() |
|||
.iter() |
|||
.filter(|c| is_ascii_uppercase(c)) |
|||
.map(|b| *b as i32) |
|||
.zip(key_chars_cycle) |
|||
.fold(String::new(), |mut acc, (c, key_char)| { |
|||
let ci: u8 = ((c - key_char + 26) % 26) as u8; |
|||
acc.push(char::from(b'A' + ci)); |
|||
acc |
|||
}) |
|||
} |
} |
||
fn main() { |
fn main() { |
||
let enc = CRYPTOGRAM |
|||
let text = "Beware the Jabberwock, my son! The jaws that bite, the claws that catch!"; |
|||
.split_ascii_whitespace() |
|||
let key = "VIGENERECIPHER"; |
|||
.collect::<Vec<_>>() |
|||
.join(""); |
|||
let cryptogram: Vec<u8> = enc.as_bytes().iter().map(|b| u8::from(b - b'A')).collect(); |
|||
println!("Key: {}", key); |
|||
let mut best_fit = std::f32::MAX; |
|||
let |
let mut best_key = String::new(); |
||
for j in 1..=26 { |
|||
println!("Code: {}", encoded); |
|||
let mut key = vec!['\0'; j]; |
|||
let fit = freq_every_nth(&cryptogram, &mut key); |
|||
println!("Back: {}", decoded); |
|||
let s_key = String::from_iter(key); // 'from_iter' is imported from std::iter::FromIterator; |
|||
}</lang> |
|||
if fit < best_fit { |
|||
best_fit = fit; |
|||
=={{header|Scala}}== |
|||
best_key = s_key; |
|||
Valid characters for messages: A through Z, zero, 1 to 9, and full-stop (.) |
|||
} |
|||
<lang scala> |
|||
object Vigenere { |
|||
def encrypt(msg: String, key: String) : String = { |
|||
var result: String = "" |
|||
var j = 0 |
|||
for (i <- 0 to msg.length - 1) { |
|||
val c = msg.charAt(i) |
|||
if (c >= 'A' && c <= 'Z') { |
|||
result += ((c + key.charAt(j) - 2 * 'A') % 26 + 'A').toChar |
|||
j = (j + 1) % key.length |
|||
} |
|||
} |
} |
||
println!("best key: {}", &best_key); |
|||
return result |
|||
println!("\nDecrypted text:\n{}", decrypt(&enc, &best_key)); |
|||
} |
|||
def decrypt(msg: String, key: String) : String = { |
|||
var result: String = "" |
|||
var j = 0 |
|||
for (i <- 0 to msg.length - 1) { |
|||
val c = msg.charAt(i) |
|||
if (c >= 'A' && c <= 'Z') { |
|||
result += ((c - key.charAt(j) + 26) % 26 + 'A').toChar |
|||
j = (j + 1) % key.length |
|||
} |
|||
} |
|||
return result |
|||
} |
|||
} |
} |
||
</syntaxhighlight> |
|||
println("Encrypt text ABC => " + Vigenere.encrypt("ABC", "KEY")) |
|||
println("Decrypt text KFA => " + Vigenere.decrypt("KFA", "KEY")) |
|||
</lang> |
|||
{{out}} |
|||
<pre>scala> Encrypt text ABC => KFA |
|||
scala> Decrypt text KFA => ABC</pre> |
|||
=={{header|Seed7}}== |
|||
<lang seed7>$ include "seed7_05.s7i"; |
|||
const func string: vigenereCipher (in string: source, in var string: keyword) is func |
|||
result |
|||
var string: dest is ""; |
|||
local |
|||
var char: ch is ' '; |
|||
var integer: index is 1; |
|||
var integer: shift is 0; |
|||
begin |
|||
keyword := upper(keyword); |
|||
for ch range source do |
|||
if ch in {'A' .. 'Z'} | {'a' .. 'z'} then |
|||
shift := ord(keyword[succ(pred(index) rem length(keyword))]) - ord('A'); |
|||
dest &:= chr(ord('A') + (ord(upper(ch)) - ord('A') + shift) rem 26); |
|||
incr(index); |
|||
end if; |
|||
end for; |
|||
end func; |
|||
const func string: vigenereDecipher (in string: source, in var string: keyword) is func |
|||
result |
|||
var string: dest is ""; |
|||
local |
|||
var char: ch is ' '; |
|||
var integer: index is 0; |
|||
var integer: shift is 0; |
|||
begin |
|||
keyword := upper(keyword); |
|||
for ch key index range source do |
|||
if ch in {'A' .. 'Z'} | {'a' .. 'z'} then |
|||
shift := ord(keyword[succ(pred(index) rem length(keyword))]) - ord('A'); |
|||
dest &:= chr(ord('A') + (ord(upper(ch)) - ord('A') - shift) mod 26); |
|||
end if; |
|||
end for; |
|||
end func; |
|||
const proc: main is func |
|||
local |
|||
const string: input is "Beware the Jabberwock, my son! The jaws that bite, the claws that catch!"; |
|||
const string: keyword is "VIGENERECIPHER"; |
|||
var string: encrypted is ""; |
|||
var string: decrypted is ""; |
|||
begin |
|||
writeln("Input: " <& input); |
|||
writeln("key: " <& keyword); |
|||
encrypted := vigenereCipher(input, keyword); |
|||
writeln("Encrypted: " <& encrypted); |
|||
decrypted := vigenereDecipher(encrypted, keyword); |
|||
writeln("Decrypted: " <& decrypted); |
|||
end func;</lang> |
|||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
best key: THECHESHIRECAT |
|||
Input: Beware the Jabberwock, my son! The jaws that bite, the claws that catch! |
|||
key: VIGENERECIPHER |
|||
Encrypted: WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY |
|||
Decrypted: BEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCH |
|||
</pre> |
|||
Decrypted text: |
|||
=={{header|Sidef}}== |
|||
THISWASTHEPOEMTHATALICEREADJABBERWOCKYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIMBLEINTHEWABEALLMIMSYWERETHEBOROGOVESANDTHEMOMERATHSOUTGRABEBEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCHBEWARETHEJUBJUBBIRDANDSHUNTHEFRUMIOUSBANDERSNATCHHETOOKHISVORPALSWORDINHANDLONGTIMETHEMANXOMEFOEHESOUGHTSORESTEDHEBYTHETUMTUMTREEANDSTOODAWHILEINTHOUGHTANDASINUFFISHTHOUGHTHESTOODTHEJABBERWOCKWITHEYESOFFLAMECAMEWHIFFLINGTHROUGHTHETULGEYWOODANDBURBLEDASITCAMEONETWOONETWOANDTHROUGHANDTHROUGHTHEVORPALBLADEWENTSNICKERSNACKHELEFTITDEADANDWITHITSHEADHEWENTGALUMPHINGBACKANDHASTTHOUSLAINTHEJABBERWOCKCOMETOMYARMSMYBEAMISHBOYOFRABJOUSDAYCALLOOHCALLAYHECHORTLEDINHISJOYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIMBLEINTHEWABEALLMIMSYWERETHEBOROGOVESANDTHEMOMERATHSOUTGRABEITSEEMSVERYPRETTYSHESAIDWHENSHEHADFINISHEDITBUTITSRATHERHARDTOUNDERSTAND |
|||
{{trans|Perl 6}} |
|||
<lang ruby>func s2v(s) { s.uc.scan(/[A-Z]/).map{.ord} »-» 65 } |
|||
func v2s(v) { v »%» 26 »+» 65 -> map{.chr}.join } |
|||
func blacken (red, key) { v2s(s2v(red) »+« s2v(key)) } |
|||
func redden (blk, key) { v2s(s2v(blk) »-« s2v(key)) } |
|||
var red = "Beware the Jabberwock, my son! The jaws that bite, the claws that catch!" |
|||
var key = "Vigenere Cipher!!!" |
|||
say red |
|||
say (var black = blacken(red, key)) |
|||
say redden(black, key)</lang> |
|||
{{out}} |
|||
<pre> |
|||
Beware the Jabberwock, my son! The jaws that bite, the claws that catch! |
|||
WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY |
|||
BEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCH |
|||
</pre> |
</pre> |
||
=={{header| |
=={{header|Tcl}}== |
||
{{trans|Python}} |
|||
in the following code, the cypher should consist of upper-case characters only. If that is not guaranteed, apply prep to it before passing it to encrypt/decrypt.. |
|||
<syntaxhighlight lang="tcl">package require Tcl 8.6 |
|||
{{works with|Smalltalk/X}} |
|||
<lang smalltalk> |
|||
prep := [:s | s select:[:ch | ch isLetter] thenCollect:[:ch | ch asUppercase]]. |
|||
encrypt := [:s :cypher | (prep value:s) keysAndValuesCollect:[:i :ch | ch rot:((cypher at:((i-1)\\key size+1))-$A) ]]. |
|||
decrypt := [:s :cypher | (prep value:s) keysAndValuesCollect:[:i :ch | ch rot:26-((cypher at:((i-1)\\key size+1))-$A) ]]. |
|||
</lang> |
|||
Test: |
|||
<lang smalltalk> |
|||
plain := 'Beware the Jabberwock, my son! The jaws that bite, the claws that catch!'. |
|||
cypher := 'VIGENERECIPHER'. |
|||
crypted := encrypt value:plain value:cypher. |
|||
plain2 := decrypt value:crypted value:cypher. |
|||
oo::class create VigenereAnalyzer { |
|||
crypted -> 'WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY' |
|||
variable letterFrequencies sortedTargets |
|||
plain2 -> 'BEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCH' |
|||
constructor {{frequencies { |
|||
</lang> |
|||
0.08167 0.01492 0.02782 0.04253 0.12702 0.02228 0.02015 |
|||
0.06094 0.06966 0.00153 0.00772 0.04025 0.02406 0.06749 |
|||
=={{header|Swift}}== |
|||
0.07507 0.01929 0.00095 0.05987 0.06327 0.09056 0.02758 |
|||
0.00978 0.02360 0.00150 0.01974 0.00074 |
|||
Can support a larger range of characters, if desired |
|||
}}} { |
|||
set letterFrequencies $frequencies |
|||
<lang swift>public func convertToUnicodeScalars( |
|||
set sortedTargets [lsort -real $frequencies] |
|||
str: String, |
|||
if {[llength $frequencies] != 26} { |
|||
minChar: UInt32, |
|||
error "wrong length of frequency table" |
|||
maxChar: UInt32 |
|||
} |
|||
) -> [UInt32] { |
|||
var scalars = [UInt32]() |
|||
for scalar in str.unicodeScalars { |
|||
let val = scalar.value |
|||
guard val >= minChar && val <= maxChar else { |
|||
continue |
|||
} |
} |
||
### Utility methods |
|||
scalars.append(val) |
|||
# Find the value of $idxvar in the range [$from..$to) that maximizes the value |
|||
} |
|||
# in $scorevar (which is computed by evaluating $body) |
|||
method Best {idxvar from to scorevar body} { |
|||
return scalars |
|||
upvar 1 $idxvar i $scorevar s |
|||
} |
|||
set bestI $from |
|||
for {set i $from} {$i < $to} {incr i} { |
|||
public struct Vigenere { |
|||
uplevel 1 $body |
|||
private let keyScalars: [UInt32] |
|||
if {![info exist bestS] || $bestS < $s} { |
|||
private let smallestScalar: UInt32 |
|||
set bestI $i |
|||
private let largestScalar: UInt32 |
|||
set bestS $s |
|||
private let sizeAlphabet: UInt32 |
|||
} |
|||
} |
|||
public init?(key: String, smallestCharacter: Character = "A", largestCharacter: Character = "Z") { |
|||
return $bestI |
|||
let smallScalars = smallestCharacter.unicodeScalars |
|||
let largeScalars = largestCharacter.unicodeScalars |
|||
guard smallScalars.count == 1, largeScalars.count == 1 else { |
|||
return nil |
|||
} |
} |
||
# Simple list map |
|||
method Map {var list body} { |
|||
self.smallestScalar = smallScalars.first!.value |
|||
upvar 1 $var v |
|||
self.largestScalar = largeScalars.first!.value |
|||
set result {} |
|||
self.sizeAlphabet = (largestScalar - smallestScalar) + 1 |
|||
foreach v $list {lappend result [uplevel 1 $body]} |
|||
return $result |
|||
let scalars = convertToUnicodeScalars(str: key, minChar: smallestScalar, maxChar: largestScalar) |
|||
guard !scalars.isEmpty else { |
|||
return nil |
|||
} |
} |
||
# Simple partition of $list into $groups groups; thus, the partition of |
|||
# {a b c d e f} into 3 produces {a d} {b e} {c f} |
|||
self.keyScalars = scalars |
|||
method Partition {list groups} { |
|||
set i 0 |
|||
foreach val $list { |
|||
dict lappend result $i $val |
|||
public func decrypt(_ str: String) -> String? { |
|||
if {[incr i] >= $groups} { |
|||
let txtBytes = convertToUnicodeScalars(str: str, minChar: smallestScalar, maxChar: largestScalar) |
|||
set i 0 |
|||
} |
|||
guard !txtBytes.isEmpty else { |
|||
} |
|||
return nil |
|||
return [dict values $result] |
|||
} |
} |
||
### Helper methods |
|||
# Get the actual counts of different types of characters in the given list |
|||
method Frequency cleaned { |
|||
for (i, c) in txtBytes.enumerated() where c >= smallestScalar && c <= largestScalar { |
|||
for {set i 0} {$i < 26} {incr i} { |
|||
dict set tbl $i 0 |
|||
UnicodeScalar((c &+ sizeAlphabet &- keyScalars[i % keyScalars.count]) % sizeAlphabet &+ smallestScalar) |
|||
} |
|||
else { |
|||
foreach ch $cleaned { |
|||
return nil |
|||
dict incr tbl [expr {[scan $ch %c] - 65}] |
|||
} |
|||
} |
|||
return $tbl |
|||
res += String(char) |
|||
} |
} |
||
# Get the correlation factor of the characters in a given list with the |
|||
return res |
|||
# class-specified language frequency corpus |
|||
} |
|||
method Correlation cleaned { |
|||
set result 0.0 |
|||
public func encrypt(_ str: String) -> String? { |
|||
set freq [lsort -integer [dict values [my Frequency $cleaned]]] |
|||
let txtBytes = convertToUnicodeScalars(str: str, minChar: smallestScalar, maxChar: largestScalar) |
|||
foreach f $freq s $sortedTargets { |
|||
set result [expr {$result + $f * $s}] |
|||
guard !txtBytes.isEmpty else { |
|||
} |
|||
return nil |
|||
return $result |
|||
} |
} |
||
# Compute an estimate for the key length |
|||
var res = "" |
|||
method GetKeyLength {cleaned {required 20}} { |
|||
# Assume that we need at least 20 characters per column to guess |
|||
for (i, c) in txtBytes.enumerated() where c >= smallestScalar && c <= largestScalar { |
|||
set bestLength [my Best i 2 [expr {[llength $cleaned] / $required}] corr { |
|||
guard let char = |
|||
set corr [expr {-0.5 * $i}] |
|||
UnicodeScalar((c &+ keyScalars[i % keyScalars.count] &- 2 &* smallestScalar) % sizeAlphabet &+ smallestScalar) |
|||
foreach chars [my Partition $cleaned $i] { |
|||
else { |
|||
set corr [expr {$corr + [my Correlation $chars]}] |
|||
return nil |
|||
} |
|||
res += String(char) |
|||
} |
|||
return res |
|||
} |
|||
} |
|||
let text = "Beware the Jabberwock, my son! The jaws that bite, the claws that catch!"; |
|||
let key = "VIGENERECIPHER"; |
|||
let cipher = Vigenere(key: key)! |
|||
print("Key: \(key)") |
|||
print("Plain Text: \(text)") |
|||
let encoded = cipher.encrypt(text.uppercased())! |
|||
print("Cipher Text: \(encoded)") |
|||
let decoded = cipher.decrypt(encoded)! |
|||
print("Decoded: \(decoded)") |
|||
print("\nLarger set:") |
|||
let key2 = "Vigenère cipher" |
|||
let text2 = "This is a ünicode string 😃" |
|||
let cipher2 = Vigenere(key: key2, smallestCharacter: " ", largestCharacter: "🛹")! |
|||
print("Key: \(key2)") |
|||
print("Plain Text: \(text2)") |
|||
let encoded2 = cipher2.encrypt(text2)! |
|||
print("Cipher Text: \(encoded2)") |
|||
let decoded2 = cipher2.decrypt(encoded2)! |
|||
print("Decoded: \(decoded2)")</lang> |
|||
{{out}} |
|||
<pre>Key: VIGENERECIPHER |
|||
Plain Text: Beware the Jabberwock, my son! The jaws that bite, the claws that catch! |
|||
Cipher Text: WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY |
|||
Decoded: BEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCH |
|||
Larger set: |
|||
Key: Vigenère cipher |
|||
Plain Text: This is a ünicode string 😃 |
|||
Cipher Text: �±°¸nıÅeacŅ¾±¨Á�®g¸Âĺ»³gc🙌 |
|||
Decoded: This is a ünicode string 😃</pre> |
|||
=={{header|Tcl}}== |
|||
{{trans|C++}} |
|||
<lang tcl>package require Tcl 8.6 |
|||
oo::class create Vigenere { |
|||
variable key |
|||
constructor {protoKey} { |
|||
foreach c [split $protoKey ""] { |
|||
if {[regexp {[A-Za-z]} $c]} { |
|||
lappend key [scan [string toupper $c] %c] |
|||
} |
} |
||
}] |
|||
if {$bestLength == 0} { |
|||
error "text is too short to analyze" |
|||
} |
} |
||
return $bestLength |
|||
} |
} |
||
# Compute the key from the given frequency tables and the class-specified |
|||
method encrypt {text} { |
|||
# language frequency corpus |
|||
set out "" |
|||
method GetKeyFromFreqs freqs { |
|||
set j 0 |
|||
foreach |
foreach f $freqs { |
||
set m [my Best i 0 26 corr { |
|||
set corr 0.0 |
|||
scan [string toupper $c] %c c |
|||
foreach {ch count} $f { |
|||
append out [format %c [expr {($c+[lindex $key $j]-130)%26+65}]] |
|||
set |
set d [expr {($ch - $i) % 26}] |
||
set corr [expr {$corr + $count*[lindex $letterFrequencies $d]}] |
|||
} |
|||
}] |
|||
append key [format %c [expr {65 + $m}]] |
|||
} |
} |
||
return $ |
return $key |
||
} |
} |
||
##### The main analyzer method ##### |
|||
method decrypt {text} { |
|||
method analyze input { |
|||
set out "" |
|||
# Turn the input into a clean letter sequence |
|||
set j 0 |
|||
set cleaned [regexp -all -inline {[A-Z]} [string toupper $input]] |
|||
foreach c [split $text ""] { |
|||
# Get the (estimated) key length |
|||
if {[regexp {[^A-Z]} $c]} continue |
|||
set bestLength [my GetKeyLength $cleaned] |
|||
scan $c %c c |
|||
# Get the frequency mapping for the partitioned input text |
|||
append out [format %c [expr {($c-[lindex $key $j]+26)%26+65}]] |
|||
set freqs [my Map p [my Partition $cleaned $bestLength] {my Frequency $p}] |
|||
set j [expr {($j+1) % [llength $key]}] |
|||
# Get the key itself |
|||
} |
|||
return $ |
return [my GetKeyFromFreqs $freqs] |
||
} |
} |
||
}</ |
}</syntaxhighlight> |
||
Demonstration (that assumes that the Tcl solution to [[Vigenère cipher#Tcl|Vigenère cipher]] task is present): |
|||
Demonstrating: |
|||
<lang |
<syntaxhighlight lang="tcl">set encoded " |
||
MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH |
|||
set original "Beware the Jabberwock, my son! The jaws that bite, the claws that catch!" |
|||
VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD |
|||
set encrypted [$cypher encrypt $original] |
|||
ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS |
|||
set decrypted [$cypher decrypt $encrypted] |
|||
FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG |
|||
puts $original |
|||
ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ |
|||
puts "Encrypted: $encrypted" |
|||
ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS |
|||
puts "Decrypted: $decrypted"</lang> |
|||
JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT |
|||
{{out}} |
|||
LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST |
|||
<pre> |
|||
MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH |
|||
Beware the Jabberwock, my son! The jaws that bite, the claws that catch! |
|||
QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV |
|||
Encrypted: WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY |
|||
RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW |
|||
Decrypted: BEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCH |
|||
TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO |
|||
</pre> |
|||
SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR |
|||
ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX |
|||
BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB |
|||
BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA |
|||
FWAML ZZRXJ EKAHV FASMU LVVUT TGK |
|||
" |
|||
VigenereAnalyzer create englishVigenereAnalyzer |
|||
set key [englishVigenereAnalyzer analyze $encoded] |
|||
Vigenere create decoder $key |
|||
set decoded [decoder decrypt $encoded] |
|||
puts "Key: $key" |
|||
puts "Text: $decoded"</syntaxhighlight> |
|||
=={{header| |
=={{header|Vedit macro language}}== |
||
This implementation is fully autonomous as long as the text is long enough and there are not too many non-English words in the original text. |
|||
The text to be analysed must be in current edit buffer. A new buffer is opened to display the results. |
|||
<lang txr>@(next :args) |
|||
@(do |
|||
(defun vig-op (plus-or-minus) |
|||
(op + #\A [mod [plus-or-minus (- @1 #\A) (- @2 #\A)] 26])) |
|||
To automatically find the best key, a dictionary is used to find English words within the decrypted text. |
|||
(defun vig (msg key encrypt) |
|||
I have used unixdict.txt, but if you do not have it available, you can use the Scribe English dictionary that comes with Vedit. |
|||
(mapcar (vig-op [if encrypt + -]) msg (repeat key)))) |
|||
However, that is unnecessarily big. A smaller dictionary is faster and may actually give better results. |
|||
@(coll)@{key /[A-Za-z]/}@(end) |
|||
It might be good idea to use dictionary that only contains the most common English words. |
|||
@(coll)@{msg /[A-Za-z]/}@(end) |
|||
@(cat key "") |
|||
@(filter :upcase key) |
|||
@(cat msg "") |
|||
@(filter :upcase msg) |
|||
@(bind encoded @(vig msg key t)) |
|||
@(bind decoded @(vig msg key nil)) |
|||
@(bind check @(vig encoded key nil)) |
|||
@(output) |
|||
text: @msg |
|||
key: @key |
|||
enc: @encoded |
|||
dec: @decoded |
|||
check: @check |
|||
@(end)</lang> |
|||
This implementation finds the best and 2nd best Caesar key for each key position. |
|||
Here, the TXR pattern language is used to scan letters out of two arguments, |
|||
It then checks key combinations where max one char is taken from 2nd best Caesar key. |
|||
and convert them to upper case. |
|||
If this does not solve some encrypted text, you could increase the number of key combinations to be checked. |
|||
The embedded TXR Lisp dialect handles the Vigenère logic, |
|||
in just a few lines of code. |
|||
<syntaxhighlight lang="vedit">// (1) Copy text into tmp buffer and remove non-alpha chars. |
|||
Lisp programmers may do a "double take" at what is going on here: yes <code>mapcar</code> can operate on strings and return strings in TXR Lisp. <code>(repeat key)</code> produces an infinite lazy list; but that's okay because <code>mapcar</code> stops after the shortest input runs out of items. |
|||
Chdir(PATH_ONLY) |
|||
Run: |
|||
BOF |
|||
Reg_Copy(10, ALL) // copy text to new buffer |
|||
Buf_Switch(Buf_Free) |
|||
Reg_Ins(10) |
|||
BOF |
|||
Replace ("|!|A", "", BEGIN+ALL+NOERR) // remove non-alpha chars |
|||
Reg_Copy_Block(10,0,EOB_pos) // @10 = text to be analysed |
|||
#20 = Buf_Num // buffer for text being analyzed |
|||
<pre>$ txr vigenere.txr 'vigenere cipher' 'Beware the Jabberwock... The jaws that... the claws that catch!' |
|||
#21 = Buf_Free // buffer for English frequency list (A-Z) |
|||
text: BEWARETHEJABBERWOCKTHEJAWSTHATTHECLAWSTHATCATCH |
|||
Buf_Switch(#21) |
|||
key: VIGENERECIPHER |
|||
Ins_Text("8167 1492 2782 4253 12702 2228 2015 6094 6966 153 772 4025 2406 6749 7507 1929 95 5987 6327 9056 2758 978 2360 150 1974 74") |
|||
enc: WMCEEIKLGRPIFVMEUGXXYILILZXYVBZLRGCEYAIOEKXIZGU |
|||
File_Open("unixdict.txt") // or use "|(MACRO_DIR)\scribe\english.vdf" |
|||
dec: GWQWEACDCBLUXNWOIYXPQAHSHLPQFLNDRYUWUKEAWCHSNYU |
|||
#23 = Buf_Num // buffer for dictionary |
|||
check: BEWARETHEJABBERWOCKTHEJAWSTHATTHECLAWSTHATCATCH</pre> |
|||
#24 = Buf_Free // buffer for key canditates |
|||
Buf_Switch(#24) |
|||
=={{header|TypeScript}}== |
|||
for (#1=0; #1<5; #1++) { // Fill table for 5 keys of 50 chars |
|||
<lang JavaScript>class Vigenere { |
|||
Ins_Char('.', COUNT, 50) |
|||
Ins_Newline |
|||
} |
|||
#22 = Buf_Free // buffer for results |
|||
#25 = Reg_Size(10) // number of letters in the text |
|||
key: string |
|||
#26 = 26 // number of characters in the alphabet |
|||
#61 = min(#25/10, 50) // max key length to try |
|||
// (2) Check Index of coincidence (or Kp) for each key length |
|||
constructor(key: string) { |
|||
this.key = Vigenere.formatText(key) |
|||
} |
|||
Buf_Switch(#22) // buffer for results |
|||
/** Enrypt a given text using key */ |
|||
Ins_Text("KeyLen Kp dist ") Ins_Newline |
|||
encrypt(plainText: string): string { |
|||
Ins_Text("-----------------") Ins_Newline |
|||
return Array.prototype.map.call(Vigenere.formatText(plainText), (letter: string, index: number): string => { |
|||
#13 = Cur_Pos |
|||
return String.fromCharCode((letter.charCodeAt(0) + this.key.charCodeAt(index % this.key.length) - 130) % 26 + 65) |
|||
#7 = 0 // no Caesar encryption |
|||
}).join('') |
|||
for (#5=1; #5<=#61; #5++) { |
|||
Buf_Switch(#20) // text being analyzed |
|||
BOF |
|||
#54 = 0; // sum of Kp's |
|||
for (#6=0; #6<#5; #6++) { // for each slide |
|||
Goto_Pos(#6) |
|||
Call("CHARACTER_FREQUENCIES") |
|||
Call("INDEX_OF_COINCIDENCE") // #51 = Kp * 10000 |
|||
#54 += #51 |
|||
} |
} |
||
#54 /= #5 // average of Kp's |
|||
Buf_Switch(#22) |
|||
Num_Ins(#5, COUNT, 3) // write key length |
|||
IT(": ") |
|||
Num_Ins(#54, NOCR) // average Kp |
|||
Num_Ins(670-#54) // distance to English Kp |
|||
} |
|||
Buf_Switch(#22) |
|||
Sort_Merge("5,12", #13, Cur_Pos, REVERSE) // sort the results by Kp value |
|||
Ins_Newline |
|||
// (3) Check the best 4 key lengths to find which one gives the best decrypt result |
|||
/** Decrypt ciphertext based on key */ |
|||
decrypt(cipherText: string): string { |
|||
return Array.prototype.map.call(Vigenere.formatText(cipherText), (letter: string, index: number): string => { |
|||
return String.fromCharCode((letter.charCodeAt(0) - this.key.charCodeAt(index % this.key.length) + 26) % 26 + 65) |
|||
}).join('') |
|||
} |
|||
#38 = 0 // max number of correct characters found |
|||
/** Converts to uppercase and removes non characters */ |
|||
#19 = 1 // best key length |
|||
private static formatText(text: string): string { |
|||
for (#14 = 0; #14<4; #14++) { // try 4 best key lengths |
|||
return text.toUpperCase().replace(/[^A-Z]/g, "") |
|||
Buf_Switch(#22) // results buffer |
|||
Goto_Pos(#13) Line(#14) |
|||
#5 = Num_Eval(SUPPRESS) // #5 = key length |
|||
Call("FIND_KEYS") // find Caesar key for each key character |
|||
#4 = -1 // try best match key chars only |
|||
Call("BUILD_KEY") |
|||
EOF |
|||
Ins_Text("Key length ") |
|||
Num_Ins(#5, LEFT) |
|||
Reg_Ins(10) // encrypted text |
|||
BOL |
|||
Call("DECRYPT_LINE") |
|||
BOL |
|||
Call("FIND_ENGLISH_WORDS") // #37 = number of English chars |
|||
EOL Ins_Newline |
|||
Ins_Text("Correct chars: ") |
|||
Num_Ins(#37) |
|||
if (#37 > #38) { |
|||
#38 = #37 |
|||
#19 = #5 |
|||
} |
} |
||
Update() |
|||
} |
} |
||
Ins_Text("Using key length: ") Num_Ins(#19) Ins_Newline |
|||
/** Example usage */ |
|||
#5 = #19 |
|||
Call("FIND_KEYS") // find Caesar key for each key character |
|||
let original: string = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book." |
|||
// (4) Decrypt with different key combinations and try to find English words. |
|||
console.log(`Original: ${original}`) |
|||
// Try key combinations where max one char is taken from 2nd best Caesar key. |
|||
#38 = 0 // max number of chars in English words found |
|||
let vig: Vigenere = new Vigenere("vigenere") |
|||
#39 = -1 // best key number found |
|||
for (#4 = -1; #4 < #19; #4++) |
|||
{ |
|||
Call("BUILD_KEY") |
|||
Buf_Switch(#22) // results |
|||
Reg_Ins(10) // encrypted text |
|||
BOL |
|||
Call("DECRYPT_LINE") |
|||
BOL |
|||
Update() |
|||
Call("FIND_ENGLISH_WORDS") // #37 := number of correct letters in text |
|||
if (#37 > #38) { |
|||
#38 = #37 // new highest number of correct chars |
|||
#39 = #4 // new best key |
|||
} |
|||
EOL IT(" -- ") // display results |
|||
let encoded: string = vig.encrypt(original) |
|||
Num_Ins(#4, COUNT, 3) // key number |
|||
Ins_Text(": ") |
|||
for (#6=0; #6<#19; #6++) { // display key |
|||
#9 = 130 + #6 |
|||
Ins_Char(#@9) |
|||
} |
|||
Ins_Text(" correct chars =") |
|||
Num_Ins(#37) |
|||
} |
|||
Ins_Text("Best key = ") |
|||
Num_Ins(#39, LEFT) |
|||
#4 = #39 |
|||
Ins_Newline |
|||
// Display results |
|||
console.log(`After encryption: ${encoded}`) |
|||
// |
|||
Buf_Switch(#24) // table for key canditates |
|||
BOF |
|||
Reg_Copy_Block(14, Cur_Pos, Cur_Pos+#19) // best Caesar key chars |
|||
Line(1) |
|||
Reg_Copy_Block(15, Cur_Pos, Cur_Pos+#19) // 2nd best Caesar key chars |
|||
Call("BUILD_KEY") |
|||
Buf_Switch(#22) |
|||
Ins_Text("Key 1: ") Reg_Ins(14) Ins_Newline |
|||
Ins_Text("Key 2: ") Reg_Ins(15) Ins_Newline |
|||
Ins_Text("Key: ") |
|||
for (#6=0; #6 < #19; #6++) { |
|||
#9 = #6+130 |
|||
Ins_Char(#@9) |
|||
} |
|||
Ins_Newline |
|||
Ins_Newline |
|||
// decrypt the text with selected key |
|||
let back: string = vig.decrypt(encoded) |
|||
Ins_Text("Decrypted text:") Ins_Newline |
|||
Reg_Ins(10) |
|||
BOL |
|||
Call("DECRYPT_LINE") |
|||
BOL Reg_Copy(13,1) |
|||
EOL Ins_Newline |
|||
// Find English words from the text |
|||
console.log(`After decryption: ${back}`) |
|||
Reg_Ins(13) |
|||
Call("FIND_ENGLISH_WORDS") |
|||
EOL |
|||
Ins_Newline |
|||
Num_Ins(#37, NOCR) IT(" of ") |
|||
Num_Ins(#25, NOCR) IT(" characters are English words. ") |
|||
Ins_Newline |
|||
Buf_Switch(#20) Buf_Quit(OK) |
|||
})() |
|||
Buf_Switch(#21) Buf_Quit(OK) |
|||
</lang> |
|||
Buf_Switch(#23) Buf_Quit(OK) |
|||
Buf_Switch(#24) Buf_Quit(OK) |
|||
Statline_Message("Done!") |
|||
=={{header|VBA}}== |
|||
Return |
|||
<lang vb>Option Explicit |
|||
///////////////////////////////////////////////////////////////////////////// |
|||
Sub test() |
|||
// |
|||
Dim Encryp As String |
|||
// Caesar decrypt current line and count character frequencies. |
|||
Encryp = Vigenere("Beware the Jabberwock, my son! The jaws that bite, the claws that catch!", "vigenerecipher", True) |
|||
// in: #5 = step size, #7 = encryption key, #26 = num of chars in alphabet |
|||
Debug.Print "Encrypt:= """ & Encryp & """" |
|||
// out: #65...#90 = frequencies, #60 = number of chars |
|||
Debug.Print "Decrypt:= """ & Vigenere(Encryp, "vigenerecipher", False) & """" |
|||
End Sub |
|||
:CHARACTER_FREQUENCIES: |
|||
Private Function Vigenere(sWord As String, sKey As String, Enc As Boolean) As String |
|||
Save_Pos |
|||
Dim bw() As Byte, bk() As Byte, i As Long, c As Long |
|||
for (#8 = 'A'; #8<='Z'; #8++) { |
|||
Const sW As String = "ÁÂÃÄÅÇÈÉÊËÌÍÎÏÑÒÓÔÕÖÙÚÛÜÝ" |
|||
#@8 = 0 // reset frequency counters |
|||
Const sWo As String = "AAAAACEEEEIIIINOOOOOUUUUY" |
|||
} |
|||
Const A As Long = 65 |
|||
#60 = 0 // total number of chars |
|||
Const N As Long = 26 |
|||
while (!At_EOL) { |
|||
if (Cur_Char >= 'A' && Cur_Char <= 'Z') { |
|||
#8 = (Cur_Char-'A'+#26-#7) % #26 + 'A' // decrypted char |
|||
#@8++ |
|||
#60++ |
|||
} |
|||
Char(#5) |
|||
} |
|||
Restore_Pos |
|||
Return |
|||
// Calculate Index of Coincidence (Kp). |
|||
c = Len(sKey) |
|||
// in: character frequencies in #65...#90, #60 = num of chars |
|||
i = Len(sWord) |
|||
// out: #51 = IC * 10000 |
|||
sKey = Left(IIf(c < i, StrRept(sKey, (i / c) + 1), sKey), i) |
|||
// |
|||
sKey = StrConv(sKey, vbUpperCase) 'Upper case |
|||
:INDEX_OF_COINCIDENCE: |
|||
sWord = StrConv(sWord, vbUpperCase) |
|||
Num_Push(10,15) |
|||
sKey = StrReplace(sKey, sW, sWo) 'Replace accented characters |
|||
#10 = 0 |
|||
sWord = StrReplace(sWord, sW, sWo) |
|||
for (#11 = 'A'; #11<='Z'; #11++) { |
|||
sKey = RemoveChars(sKey) 'Remove characters (numerics, spaces, comas, ...) |
|||
#10 += (#@11 * (#@11-1)) // Calculate sigma{ni * (ni-1)} |
|||
sWord = RemoveChars(sWord) |
|||
} |
|||
bk = CharToAscii(sKey) 'To work with Bytes instead of String |
|||
#12 = #60 * (#60-1) // #12 = N * (N-1) |
|||
bw = CharToAscii(sWord) |
|||
#51 = #10 * 10000 / #12 // #51 = Kp * 10000 |
|||
For i = LBound(bw) To UBound(bw) |
|||
Num_Pop(10,15) |
|||
Vigenere = Vigenere & Chr((IIf(Enc, ((bw(i) - A) + (bk(i) - A)), ((bw(i) - A) - (bk(i) - A)) + N) Mod N) + A) |
|||
Return |
|||
Next i |
|||
End Function |
|||
// Find best and 2nd best Caesar key for each character position of Vigenère key. |
|||
Private Function StrRept(s As String, N As Long) As String |
|||
// in: #5=step size (key length) |
|||
Dim j As Long, c As String |
|||
// out: keys in buffer #24 |
|||
For j = 1 To N |
|||
// |
|||
c = c & s |
|||
:FIND_KEYS: |
|||
Next |
|||
for (#6 = 0; #6 < #5; #6++) { // for each char position in the key |
|||
StrRept = c |
|||
#30 = -1 // best key char found so far |
|||
End Function |
|||
#31 = -1 // 2nd best key char |
|||
#32 = MAXNUM // smallest error found so far |
|||
#33 = MAXNUM // 2nd smallest error found so far |
|||
for (#7 = 0; #7 < #26; #7++) { // for each possible key value |
|||
#35 = 0 // total frequency error compared to English |
|||
Buf_Switch(#20) // text being analyzed |
|||
Goto_Pos(#6) |
|||
Call("CHARACTER_FREQUENCIES") |
|||
Buf_Switch(#21) // English frequency table |
|||
BOF |
|||
for (#8 = 'A'; #8<='Z'; #8++) { // calculate total frequency error |
|||
#34 = Num_Eval(SUPPRESS+ADVANCE) |
|||
#35 += abs((#@8*100000+50000)/#60-#34) |
|||
} |
|||
if (#35 < #32) { // found better match? |
|||
Private Function StrReplace(s As String, What As String, By As String) As String |
|||
#33 = #32 |
|||
Dim t() As String, u() As String, i As Long |
|||
#32 = #35 |
|||
t = SplitString(What) |
|||
#31 = #30 |
|||
u = SplitString(By) |
|||
#30 = #7 |
|||
} else { |
|||
if (#35 < #33) { // 2nd best match? |
|||
StrReplace = Replace(StrReplace, t(i), u(i)) |
|||
#33 = #35 |
|||
Next i |
|||
#31 = #7 |
|||
End Function |
|||
} |
|||
} |
|||
} |
|||
Buf_Switch(#24) // table for key canditates |
|||
BOF |
|||
Goto_Col(#6+1) |
|||
Ins_Char(#30+'A', OVERWRITE) // save the best match |
|||
Line(1) |
|||
Goto_Col(#6+1) |
|||
Ins_Char(#31+'A', OVERWRITE) // save 2nd best match |
|||
} |
|||
Buf_Switch(#22) // results buffer |
|||
Return |
|||
// Combine actual key from 1st and 2nd best Caesar key characters |
|||
Private Function SplitString(s As String) As String() |
|||
// Use 1st key chars and (possibly) one character from 2nd key. |
|||
SplitString = Split(StrConv(s, vbUnicode), Chr(0)) |
|||
// #4 = index of the char to be picked from 2nd key, -1 = none. |
|||
End Function |
|||
// #5 = key length |
|||
// |
|||
:BUILD_KEY: |
|||
Buf_Switch(#24) // table for key canditates |
|||
BOF |
|||
for (#6=0; #6<#5; #6++) { // copy 1st key |
|||
#8 = 130 + #6 |
|||
#@8 = Cur_Char |
|||
Char(1) |
|||
} |
|||
if (#4 >= 0) { |
|||
#8 = 130 + #4 // pick one char from 2st key |
|||
Line(1) |
|||
Goto_Col(#4+1) |
|||
#@8 = Cur_Char |
|||
} |
|||
Buf_Switch(#22) // results buffer |
|||
Return |
|||
// Decrypt text on current line |
|||
Private Function RemoveChars(str As String) As String |
|||
// in: #5 = key length, #130...#189 = key |
|||
Dim b() As Byte, i As Long |
|||
// |
|||
b = CharToAscii(str) |
|||
:DECRYPT_LINE: |
|||
For i = LBound(b) To UBound(b) |
|||
Num_Push(6,9) |
|||
If b(i) >= 65 And b(i) <= 90 Then RemoveChars = RemoveChars & Chr(b(i)) |
|||
#6 = 0 |
|||
While (!At_EOL) { |
|||
End Function |
|||
#9 = #6+130 |
|||
#7 = #@9 |
|||
#8 = (Cur_Char - #7 + #26) % #26 + 'A' // decrypted char |
|||
Ins_Char(#8, OVERWRITE) |
|||
#6++ |
|||
if (#6 >= #5) { |
|||
#6 = 0 |
|||
} |
|||
} |
|||
Num_Pop(6,9) |
|||
Return |
|||
// Find English words from text on current line |
|||
Private Function CharToAscii(s As String) As Byte() |
|||
// out: #37 = number of chars matched |
|||
CharToAscii = StrConv(s, vbFromUnicode) |
|||
// |
|||
End Function</lang> |
|||
:FIND_ENGLISH_WORDS: |
|||
Buf_Switch(#23) // dictionary |
|||
BOF |
|||
While (!At_EOF) { |
|||
Reg_Copy_Block(12, Cur_Pos, EOL_Pos) |
|||
if (Reg_Size(12) > 2) { |
|||
Buf_Switch(#22) // buffer for results |
|||
BOL |
|||
while (Search_Block(@12, Cur_Pos, EOL_Pos, NOERR)) { |
|||
Reg_Ins(12, OVERWRITE) |
|||
} |
|||
Buf_Switch(#23) |
|||
} |
|||
Line(1, ERRBREAK) |
|||
} |
|||
Buf_Switch(#22) |
|||
{{Out}} |
|||
BOL |
|||
<pre>Encrypt:= "WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY" |
|||
#37 = Search_Block("|V", Cur_Pos, EOL_Pos, ALL+NOERR) |
|||
Decrypt:= "BEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCH"</pre> |
|||
Return </syntaxhighlight> |
|||
=={{header| |
=={{header|V (Vlang)}}== |
||
{{trans| |
{{trans|Go}} |
||
<syntaxhighlight lang="v (vlang)">import strings |
|||
<lang vb> |
|||
const encoded = |
|||
Function Encrypt(text,key) |
|||
"MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH" + |
|||
text = OnlyCaps(text) |
|||
"VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD" + |
|||
key = OnlyCaps(key) |
|||
"ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS" + |
|||
j = 1 |
|||
"FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG" + |
|||
For i = 1 To Len(text) |
|||
"ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ" + |
|||
ms = Mid(text,i,1) |
|||
"ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS" + |
|||
m = Asc(ms) - Asc("A") |
|||
"JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT" + |
|||
ks = Mid(key,j,1) |
|||
"LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST" + |
|||
k = Asc(ks) - Asc("A") |
|||
"MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH" + |
|||
j = (j Mod Len(key)) + 1 |
|||
"QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV" + |
|||
c = (m + k) Mod 26 |
|||
"RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW" + |
|||
c = Chr(Asc("A")+c) |
|||
"TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO" + |
|||
Encrypt = Encrypt & c |
|||
"SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR" + |
|||
Next |
|||
"ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX" + |
|||
End Function |
|||
"BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB" + |
|||
"BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA" + |
|||
"FWAML ZZRXJ EKAHV FASMU LVVUT TGK" |
|||
const freq = [ |
|||
0.08167, 0.01492, 0.02782, 0.04253, 0.12702, 0.02228, 0.02015, |
|||
0.06094, 0.06966, 0.00153, 0.00772, 0.04025, 0.02406, 0.06749, |
|||
0.07507, 0.01929, 0.00095, 0.05987, 0.06327, 0.09056, 0.02758, |
|||
0.00978, 0.02360, 0.00150, 0.01974, 0.00074, |
|||
] |
|||
fn sum(a []f64) f64 { |
|||
mut s := 0.0 |
|||
for f in a { |
|||
s += f |
|||
} |
|||
return s |
|||
} |
|||
fn best_match(a []f64) int { |
|||
s := sum(a) |
|||
mut best_fit, mut best_rotate := 1e100, 0 |
|||
for rotate in 0..26 { |
|||
mut fit := 0.0 |
|||
for i in 0..26 { |
|||
d := a[(i+rotate)%26]/s - freq[i] |
|||
fit += d * d / freq[i] |
|||
} |
|||
if fit < best_fit { |
|||
best_fit, best_rotate = fit, rotate |
|||
} |
|||
} |
|||
return best_rotate |
|||
} |
|||
fn freq_every_nth(msg []int, mut key []u8) f64 { |
|||
l := msg.len |
|||
interval := key.len |
|||
mut out := []f64{len: 26} |
|||
mut accu := []f64{len: 26} |
|||
for j in 0..interval { |
|||
for z in 0..26 { |
|||
out[z] = 0.0 |
|||
} |
|||
for i := j; i < l; i += interval { |
|||
out[msg[i]]++ |
|||
} |
|||
rot := best_match(out) |
|||
key[j] = u8(rot + 65) |
|||
for i := 0; i < 26; i++ { |
|||
accu[i] += out[(i+rot)%26] |
|||
} |
|||
} |
|||
s := sum(accu) |
|||
mut ret := 0.0 |
|||
for i := 0; i < 26; i++ { |
|||
d := accu[i]/s - freq[i] |
|||
ret += d * d / freq[i] |
|||
} |
|||
return ret |
|||
} |
|||
fn decrypt(text string, key string) string { |
|||
mut sb := strings.new_builder(128) |
|||
mut ki := 0 |
|||
for c in text { |
|||
if c < 'A'[0] || c > 'Z'[0] { |
|||
continue |
|||
} |
|||
ci := (c - key[ki] + 26) % 26 |
|||
sb.write_rune(ci + 65) |
|||
ki = (ki + 1) % key.len |
|||
} |
|||
return sb.str() |
|||
} |
|||
fn main() { |
|||
enc := encoded.replace(" ", "") |
|||
mut txt := []int{len: enc.len} |
|||
for i in 0..txt.len { |
|||
txt[i] = int(enc[i] - 'A'[0]) |
|||
} |
|||
mut best_fit, mut best_key := 1e100, "" |
|||
println(" Fit Length Key") |
|||
for j := 1; j <= 26; j++ { |
|||
mut key := []u8{len: j} |
|||
fit := freq_every_nth(txt, mut key) |
|||
s_key := key.bytestr() |
|||
print("${fit:.6} ${j:2} $s_key") |
|||
if fit < best_fit { |
|||
best_fit, best_key = fit, s_key |
|||
print(" <--- best so far") |
|||
} |
|||
println('') |
|||
} |
|||
println("\nBest key : $best_key") |
|||
println("\nDecrypted text:\n${decrypt(enc, best_key)}") |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
Function Decrypt(text,key) |
|||
Note: carriage returns inserted into decrypted text after every 80 characters to make it more readable. |
|||
key = OnlyCaps(key) |
|||
<pre> |
|||
j = 1 |
|||
Fit Length Key |
|||
For i = 1 To Len(text) |
|||
2.984348 1 E <--- best so far |
|||
ms = Mid(text,i,1) |
|||
2.483684 2 EC <--- best so far |
|||
m = Asc(ms) - Asc("A") |
|||
2.642487 3 TEE |
|||
ks = Mid(key,j,1) |
|||
1.976651 4 THEC <--- best so far |
|||
k = Asc(ks) - Asc("A") |
|||
2.356881 5 EEEPU |
|||
j = (j Mod Len(key)) + 1 |
|||
2.203129 6 TCECEC |
|||
1.051163 7 THECSAS <--- best so far |
|||
c = Chr(Asc("A")+c) |
|||
1.645763 8 TJQGAHET |
|||
Decrypt = Decrypt & c |
|||
2.001380 9 VEIZSEGNT |
|||
Next |
|||
1.824476 10 ECEGAWQTDS |
|||
End Function |
|||
1.623083 11 TNLUSRXPTAJ |
|||
1.253527 12 XLECTHQGTHEC |
|||
1.399037 13 LJJTDGFNOTENR |
|||
0.152370 14 THECHESHIRECAT <--- best so far |
|||
1.533951 15 JNTOOEEXFTGQTNH |
|||
1.068182 16 TJTSAEETEXHPXHNE |
|||
1.034093 17 AZRAXUHEJLREEXIEE |
|||
1.443345 18 VNIZQPALEPTSXSEXUC |
|||
1.090977 19 FUCAITCSLVTEZDUDEHS |
|||
0.979868 20 EQXGAHWTTQECEWUGXHPI |
|||
0.789410 21 HVRCSAFTHEBDLSTAERSES |
|||
0.881380 22 TVIJTCIGKAQPELECRXPTNC |
|||
0.952456 23 KKEQXGPWTCQEELIEHXUWASV |
|||
0.715968 24 ELAIXHQTTIEDXJETTNTGAEPC |
|||
0.891258 25 OTJUUEGERDNQTUQEAGWUTIEOA |
|||
0.852784 26 IGITEGECAGAVUNLJAHASAVTETW |
|||
Best key : THECHESHIRECAT |
|||
Function OnlyCaps(s) |
|||
For i = 1 To Len(s) |
|||
char = UCase(Mid(s,i,1)) |
|||
If Asc(char) >= 65 And Asc(char) <= 90 Then |
|||
OnlyCaps = OnlyCaps & char |
|||
End If |
|||
Next |
|||
End Function |
|||
Decrypted text: |
|||
'testing the functions |
|||
THISWASTHEPOEMTHATALICEREADJABBERWOCKYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIMB |
|||
orig_text = "Beware the Jabberwock, my son! The jaws that bite, the claws that catch!" |
|||
LEINTHEWABEALLMIMSYWERETHEBOROGOVESANDTHEMOMERATHSOUTGRABEBEWARETHEJABBERWOCKMYS |
|||
orig_key = "vigenerecipher" |
|||
ONTHEJAWSTHATBITETHECLAWSTHATCATCHBEWARETHEJUBJUBBIRDANDSHUNTHEFRUMIOUSBANDERSNA |
|||
WScript.StdOut.WriteLine "Original: " & orig_text |
|||
TCHHETOOKHISVORPALSWORDINHANDLONGTIMETHEMANXOMEFOEHESOUGHTSORESTEDHEBYTHETUMTUMT |
|||
WScript.StdOut.WriteLine "Key: " & orig_key |
|||
REEANDSTOODAWHILEINTHOUGHTANDASINUFFISHTHOUGHTHESTOODTHEJABBERWOCKWITHEYESOFFLAM |
|||
WScript.StdOut.WriteLine "Encrypted: " & Encrypt(orig_text,orig_key) |
|||
ECAMEWHIFFLINGTHROUGHTHETULGEYWOODANDBURBLEDASITCAMEONETWOONETWOANDTHROUGHANDTHR |
|||
WScript.StdOut.WriteLine "Decrypted: " & Decrypt(Encrypt(orig_text,orig_key),orig_key) |
|||
OUGHTHEVORPALBLADEWENTSNICKERSNACKHELEFTITDEADANDWITHITSHEADHEWENTGALUMPHINGBACK |
|||
</lang> |
|||
ANDHASTTHOUSLAINTHEJABBERWOCKCOMETOMYARMSMYBEAMISHBOYOFRABJOUSDAYCALLOOHCALLAYHE |
|||
CHORTLEDINHISJOYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIMBLEINTHEWABEALLMIMSYWER |
|||
{{Out}} |
|||
ETHEBOROGOVESANDTHEMOMERATHSOUTGRABEITSEEMSVERYPRETTYSHESAIDWHENSHEHADFINISHEDIT |
|||
<pre> |
|||
BUTITSRATHERHARDTOUNDERSTAND |
|||
Original: Beware the Jabberwock, my son! The jaws that bite, the claws that catch! |
|||
Key: vigenerecipher |
|||
Encrypted: WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY |
|||
Decrypted: BEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCH |
|||
</pre> |
</pre> |
||
=={{header|Wren}}== |
|||
An alternate implementation using RegExp to filter the input |
|||
{{trans|Kotlin}} |
|||
<lang vb> |
|||
{{libheader|Wren-math}} |
|||
'vigenere cypher |
|||
{{libheader|Wren-iterate}} |
|||
option explicit |
|||
{{libheader|Wren-str}} |
|||
const asca =65 'ascii(a) |
|||
{{libheader|Wren-fmt}} |
|||
<syntaxhighlight lang="wren">import "./math" for Nums |
|||
import "./iterate" for Stepped |
|||
import "./str" for Char, Str |
|||
import "./fmt" for Fmt |
|||
var encoded = |
|||
function filter(s) |
|||
"MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH" + |
|||
with new regexp |
|||
"VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD" + |
|||
.pattern="[^A-Z]" |
|||
"ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS" + |
|||
.global=1 |
|||
"FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG" + |
|||
filter=.replace(ucase(s),"") |
|||
"ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ" + |
|||
end with |
|||
"ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS" + |
|||
end function |
|||
"JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT" + |
|||
"LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST" + |
|||
"MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH" + |
|||
"QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV" + |
|||
"RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW" + |
|||
"TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO" + |
|||
"SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR" + |
|||
"ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX" + |
|||
"BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB" + |
|||
"BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA" + |
|||
"FWAML ZZRXJ EKAHV FASMU LVVUT TGK" |
|||
var freq = [ |
|||
function vigenere (s,k,sign) |
|||
0.08167, 0.01492, 0.02782, 0.04253, 0.12702, 0.02228, 0.02015, |
|||
dim s1,i,a,b |
|||
0.06094, 0.06966, 0.00153, 0.00772, 0.04025, 0.02406, 0.06749, |
|||
for i=0 to len(s)-1 |
|||
0.07507, 0.01929, 0.00095, 0.05987, 0.06327, 0.09056, 0.02758, |
|||
a=asc(mid(s,i+1,1))-asca |
|||
0.00978, 0.02360, 0.00150, 0.01974, 0.00074 |
|||
b=sign * (asc(mid(k,(i mod len(k))+1,1))-asca) |
|||
] |
|||
s1=s1 & chr(((a+b+26) mod 26) +asca) |
|||
next |
|||
vigenere=s1 |
|||
end function |
|||
var bestMatch = Fn.new { |a| |
|||
function encrypt(s,k): encrypt=vigenere(s,k,1) :end function |
|||
var sum = Nums.sum(a) |
|||
function decrypt(s,k): decrypt=vigenere(s,k,-1) :end function |
|||
var bestFit = 1e100 |
|||
var bestRotate = 0 |
|||
for (rotate in 0..25) { |
|||
var fit = 0 |
|||
for (i in 0..25) { |
|||
var d = a[(i + rotate) % 26] / sum - freq[i] |
|||
fit = fit + d * d / freq[i] |
|||
} |
|||
if (fit < bestFit) { |
|||
bestFit = fit |
|||
bestRotate = rotate |
|||
} |
|||
} |
|||
return bestRotate |
|||
} |
|||
var freqEveryNth = Fn.new { |msg, key| |
|||
'test-------------------------- |
|||
var len = msg.count |
|||
dim plaintext,filtered,key,encoded |
|||
var interval = key.count |
|||
key="VIGENERECYPHER" |
|||
var out = List.filled(26, 0) |
|||
plaintext = "Beware the Jabberwock, my son! The jaws that bite, the claws that catch!" |
|||
var accu = List.filled(26, 0) |
|||
filtered= filter(plaintext) |
|||
for (j in 0...interval) { |
|||
wscript.echo filtered |
|||
for (i in 0..25) out[i] = 0 |
|||
encoded=encrypt(filtered,key) |
|||
for (i in Stepped.new(j...len, interval)) out[msg[i]] = out[msg[i]] + 1 |
|||
wscript.echo encoded |
|||
var rot = bestMatch.call(out) |
|||
wscript.echo decrypt(encoded,key) |
|||
key[j] = Char.fromCode(rot + 65) |
|||
for (i in 0..25) accu[i] = accu[i] + out[(i + rot) % 26] |
|||
</lang> |
|||
} |
|||
var sum = Nums.sum(accu) |
|||
var ret = 0 |
|||
for (i in 0..25) { |
|||
var d = accu[i] / sum - freq[i] |
|||
ret = ret + d * d / freq[i] |
|||
} |
|||
return ret |
|||
} |
|||
var decrypt = Fn.new { |text, key| |
|||
=={{header|Vedit macro language}}== |
|||
var sb = "" |
|||
Encrypts and then decrypts one line of text on current edit buffer, |
|||
var ki = 0 |
|||
starting from cursor location. |
|||
for (c in text) { |
|||
The user enters the keyword (upper or lower case). |
|||
if (Char.isAsciiUpper(c)) { |
|||
<lang vedit>Get_Input(10, "Key: ", STATLINE+NOCR) // @10 = key |
|||
var ci = (c.bytes[0] - key[ki].bytes[0] + 26) % 26 |
|||
Reg_Copy_Block(11, Cur_Pos, EOL_Pos) // @11 = copy of original text |
|||
sb = sb + Char.fromCode(ci + 65) |
|||
EOL Ins_Newline |
|||
ki = (ki + 1) % key.count |
|||
Ins_Text("Key = ") Reg_Ins(10) Ins_Newline |
|||
} |
|||
} |
|||
// Prepare the key into numeric registers #130..: |
|||
return sb |
|||
Buf_Switch(Buf_Free) |
|||
Reg_Ins(10) |
|||
Case_Upper_Block(0, Cur_Pos) |
|||
BOF |
|||
#2 = Reg_Size(10) // #2 = key length |
|||
for (#3=130; #3 < 130+#2; #3++) { |
|||
#@3 = Cur_Char |
|||
Char(1) |
|||
} |
} |
||
Buf_Quit(OK) |
|||
var enc = encoded.replace(" ", "") |
|||
Ins_Text("Encrypted: ") |
|||
var txt = List.filled(enc.count, 0) |
|||
#4 = Cur_Pos |
|||
for (i in 0...txt.count) txt[i] = Char.code(enc[i]) - 65 |
|||
Reg_Ins(11) // copy of original text |
|||
var bestFit = 1e100 |
|||
Replace_Block("|!|A", "", #4, EOL_Pos, BEGIN+ALL+NOERR) // remove non-alpha chars |
|||
var bestKey = "" |
|||
Case_Upper_Block(#4, EOL_Pos) // convert to upper case |
|||
var f = "$f $2d $s" |
|||
Goto_Pos(#4) |
|||
System.print(" Fit Length Key") |
|||
#1 = 1; Call("ENCRYPT_DECRYPT") // Encrypt the line |
|||
for (j in 1..26) { |
|||
Reg_Copy_Block(11, #4, Cur_Pos) // Copy encrypted text text to next line |
|||
var key = List.filled(j, "") |
|||
Ins_Newline |
|||
var fit = freqEveryNth.call(txt, key) |
|||
Ins_Text("Decrypted: ") |
|||
var sKey = key.join("") |
|||
Reg_Ins(11, BEGIN) |
|||
Fmt.write(f, fit, j, sKey) |
|||
#1 = -1; Call("ENCRYPT_DECRYPT") // Decrypt the line |
|||
if (fit < bestFit) { |
|||
bestFit = fit |
|||
Return |
|||
bestKey = sKey |
|||
System.write(" <--- best so far") |
|||
// Encrypt or decrypt text on current line in-place, starting from cursor position. |
|||
// in: #1 = direction (1=encrypt, -1=decrypt) |
|||
// #2 = key length, #130...#189 = the key |
|||
// |
|||
:ENCRYPT_DECRYPT: |
|||
Num_Push(6,9) |
|||
#6 = 0 |
|||
While (!At_EOL) { |
|||
#7 = #6+130 // pointer to key array |
|||
#8 = #@7 // get key character |
|||
#9 = (Cur_Char + #8*#1 + 26) % 26 + 'A' // decrypt/encrypt |
|||
Ins_Char(#9, OVERWRITE) // write the converted char |
|||
#6 = (#6+1) % #2 |
|||
} |
} |
||
System.print() |
|||
} |
|||
Return </lang> |
|||
System.print() |
|||
System.print("Best key : %(bestKey)") |
|||
System.print("\nDecrypted text:\n%(decrypt.call(enc, bestKey))")</syntaxhighlight> |
|||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
Fit Length Key |
|||
Beware the Jabberwock, my son! The jaws that bite, the claws that catch! |
|||
2.984348 1 E <--- best so far |
|||
Key = vigenerecipher |
|||
2.483684 2 EC <--- best so far |
|||
Encrypted: WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY |
|||
2.642487 3 TEE |
|||
Decrypted: BEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCH |
|||
1.976651 4 THEC <--- best so far |
|||
</pre> |
|||
2.356881 5 EEEPU |
|||
2.203129 6 TCECEC |
|||
1.051163 7 THECSAS <--- best so far |
|||
1.645763 8 TJQGAHET |
|||
2.001380 9 VEIZSEGNT |
|||
1.824476 10 ECEGAWQTDS |
|||
1.623083 11 TNLUSRXPTAJ |
|||
1.253527 12 XLECTHQGTHEC |
|||
1.399037 13 LJJTDGFNOTENR |
|||
0.152370 14 THECHESHIRECAT <--- best so far |
|||
1.533951 15 JNTOOEEXFTGQTNH |
|||
1.068182 16 TJTSAEETEXHPXHNE |
|||
1.034093 17 AZRAXUHEJLREEXIEE |
|||
1.443345 18 VNIZQPALEPTSXSEXUC |
|||
1.090977 19 FUCAITCSLVTEZDUDEHS |
|||
0.979868 20 EQXGAHWTTQECEWUGXHPI |
|||
0.789410 21 HVRCSAFTHEBDLSTAERSES |
|||
0.881380 22 TVIJTCIGKAQPELECRXPTNC |
|||
0.952456 23 KKEQXGPWTCQEELIEHXUWASV |
|||
0.715968 24 ELAIXHQTTIEDXJETTNTGAEPC |
|||
0.891258 25 OTJUUEGERDNQTUQEAGWUTIEOA |
|||
0.852784 26 IGITEGECAGAVUNLJAHASAVTETW |
|||
Best key : THECHESHIRECAT |
|||
=={{header|XPL0}}== |
|||
The KEYWORD must be UPPERCASE. Precede it with a minus sign to decrypt a file. |
|||
Usage: vigenere KEYWORD <infile.txt >outfile.xxx |
|||
Decrypted text: |
|||
<lang XPL0>code ChIn=7, ChOut=8; |
|||
THISWASTHEPOEMTHATALICEREADJABBERWOCKYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIMBLEINTHEWABEALLMIMSYWERETHEBOROGOVESANDTHEMOMERATHSOUTGRABEBEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCHBEWARETHEJUBJUBBIRDANDSHUNTHEFRUMIOUSBANDERSNATCHHETOOKHISVORPALSWORDINHANDLONGTIMETHEMANXOMEFOEHESOUGHTSORESTEDHEBYTHETUMTUMTREEANDSTOODAWHILEINTHOUGHTANDASINUFFISHTHOUGHTHESTOODTHEJABBERWOCKWITHEYESOFFLAMECAMEWHIFFLINGTHROUGHTHETULGEYWOODANDBURBLEDASITCAMEONETWOONETWOANDTHROUGHANDTHROUGHTHEVORPALBLADEWENTSNICKERSNACKHELEFTITDEADANDWITHITSHEADHEWENTGALUMPHINGBACKANDHASTTHOUSLAINTHEJABBERWOCKCOMETOMYARMSMYBEAMISHBOYOFRABJOUSDAYCALLOOHCALLAYHECHORTLEDINHISJOYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIMBLEINTHEWABEALLMIMSYWERETHEBOROGOVESANDTHEMOMERATHSOUTGRABEITSEEMSVERYPRETTYSHESAIDWHENSHEHADFINISHEDITBUTITSRATHERHARDTOUNDERSTAND |
|||
int Neg, C, Len, I, Key; |
|||
char KeyWord(80); |
|||
[Neg:= false; \skip to KEYWORD |
|||
repeat C:= ChIn(8); if C=^- then Neg:= true; until C>=^A & C<=^Z; |
|||
Len:= 0; \read in KEYWORD |
|||
repeat KeyWord(Len):= C-^A; Len:= Len+1; C:= ChIn(8); until C<^A ! C>^Z; |
|||
I:= 0; \initialize cycling index |
|||
repeat C:= ChIn(1); |
|||
if C>=^a & C<=^z then C:= C-$20; \capitalize |
|||
if C>=^A & C<=^Z then \discard non-alphas |
|||
[Key:= KeyWord(I); I:= I+1; if I>=Len then I:= 0; |
|||
if Neg then Key:= -Key; \decrypting? |
|||
C:= C+Key; |
|||
if C>^Z then C:= C-26 |
|||
else if C<^A then C:= C+26; |
|||
ChOut(0, C); |
|||
]; |
|||
until C=$1A; \EOF |
|||
ChOut(0, $1A); \encrypted file must end with EOF otherwise the decode will hang |
|||
]</lang> |
|||
{{out}} |
|||
<pre> |
|||
KEYWORD = ACE |
|||
infile = Pack my box with five dozen liquor jugs. |
|||
outfile = PCGKOCBQBWKXHHMVGHOBINNMQWSRLYGU |
|||
KEYWORD = -ACE |
|||
outfile = PACKMYBOXWITHFIVEDOZENLIQUORJUGS |
|||
</pre> |
</pre> |
||
=={{header|zkl}}== |
=={{header|zkl}}== |
||
{{trans| |
{{trans|Python}} |
||
<syntaxhighlight lang="zkl">var[const] uppercase=["A".."Z"].pump(String), |
|||
<lang zkl>fcn encipher(src,key,is_encode){ |
|||
english_frequences=T( // A..Z |
|||
upperCase:=["A".."Z"].pump(String); |
|||
0.08167, 0.01492, 0.02782, 0.04253, 0.12702, 0.02228, 0.02015, |
|||
src=src.toUpper().inCommon(upperCase); // only uppercase |
|||
0.06094, 0.06966, 0.00153, 0.00772, 0.04025, 0.02406, 0.06749, |
|||
key=key.toUpper().inCommon(upperCase).pump(List,"toAsc"); |
|||
0.07507, 0.01929, 0.00095, 0.05987, 0.06327, 0.09056, 0.02758, |
|||
0.00978, 0.02360, 0.00150, 0.01974, 0.00074); |
|||
const A="A".toAsc(); |
|||
klen:=Walker.cycle(key.len()); // 0,1,2,3,..,keyLen-1,0,1,2,3, ... |
|||
fcn vigenere_decrypt(target_freqs, input){ // ( (float,...), string) |
|||
src.pump(String,'wrap(c){ i:=klen.next(); c=c.toAsc(); |
|||
nchars,ordA :=uppercase.len(),"A".toAsc(); |
|||
(A + ( if(is_encode) c - A + key[i] - A; |
|||
sorted_targets:=target_freqs.sort(); |
|||
else c - key[i] + 26 ) % 26).toChar() |
|||
}); |
|||
frequency:='wrap(input){ // (n,n,n,n,...), n is ASCII index ("A"==65) |
|||
}</lang> |
|||
result:=uppercase.pump(List(),List.fp1(0)); // ( ("A",0),("B",0) ...) |
|||
<lang zkl>str := "Beware the Jabberwock, my son! The jaws that bite, " |
|||
foreach c in (input){ result[c - ordA][1] += 1 } |
|||
"the claws that catch!"; |
|||
result // --> mutable list of mutable lists ( ("A",Int)...("Z",Int) ) |
|||
key := "Vigenere Cipher"; |
|||
}; |
|||
correlation:='wrap(input){ // (n,n,n,n,...), n is ASCII index ("A"==65) |
|||
println("Text: ", str); |
|||
result,freq:=0.0, frequency(input); |
|||
println("key: ", key); |
|||
freq.sort(fcn([(_,a)],[(_,b)]){ a<b }); // sort letters by frequency |
|||
foreach i,f in (freq.enumerate()){ result+=sorted_targets[i]*f[1] } |
|||
result // -->Float |
|||
}; |
|||
cleaned:=input.toUpper().pump(List,uppercase.holds,Void.Filter,"toAsc"); |
|||
best_len,best_corr := 0,-100.0; |
|||
# Assume that if there are less than 20 characters |
|||
# per column, the key's too long to guess |
|||
foreach i in ([2..cleaned.len()/20]){ |
|||
pieces:=(i).pump(List,List.copy); // ( (),() ... ) |
|||
foreach c in (cleaned){ pieces[__cWalker.idx%i].append(c) } |
|||
# The correlation seems to increase for smaller |
|||
# pieces/longer keys, so weigh against them a little |
|||
corr:=-0.5*i + pieces.apply(correlation).sum(0.0); |
|||
if(corr>best_corr) best_len,best_corr=i,corr; |
|||
} |
|||
if(best_len==0) return("Text is too short to analyze", ""); |
|||
pieces:=best_len.pump(List,List.copy); |
|||
foreach c in (cleaned){ pieces[__cWalker.idx%best_len].append(c) } |
|||
key,freqs := "",pieces.apply(frequency); |
|||
foreach fr in (freqs){ |
|||
fr.sort(fcn([(_,a)],[(_,b)]){ a>b }); // reverse sort by freq |
|||
m,max_corr := 0,0.0; |
|||
foreach j in (nchars){ |
|||
corr,c := 0.0,ordA + j; |
|||
foreach frc in (fr){ |
|||
d:=(frc[0].toAsc() - c + nchars) % nchars; |
|||
corr+=target_freqs[d]*frc[1]; |
|||
if(corr>max_corr) m,max_corr=j,corr; |
|||
} |
|||
} |
|||
key+=(m + ordA).toChar(); |
|||
} |
|||
cleaned.enumerate().apply('wrap([(i,c])){ |
|||
cod := encipher(str, key, True); println("Code: ", cod); |
|||
( (c - (key[i%best_len]).toAsc() + nchars)%nchars + ordA ).toChar() |
|||
dec := encipher(cod, key, False); println("Back: ", dec);</lang> |
|||
}).concat() : |
|||
T(key,_); |
|||
}</syntaxhighlight> |
|||
<syntaxhighlight lang="zkl">encryptedText:= |
|||
#<<< |
|||
"MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH |
|||
VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD |
|||
ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS |
|||
FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG |
|||
ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ |
|||
ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS |
|||
JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT |
|||
LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST |
|||
MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH |
|||
QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV |
|||
RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW |
|||
TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO |
|||
SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR |
|||
ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX |
|||
BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB |
|||
BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA |
|||
FWAML ZZRXJ EKAHV FASMU LVVUT TGK"; |
|||
#<<< |
|||
key,decoded:=vigenere_decrypt(english_frequences,encryptedText); |
|||
println("Key:", key); |
|||
println("Decoded text:", decoded);</syntaxhighlight> |
|||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
Key:THECHESHIRECAT |
|||
Text: Beware the Jabberwock, my son! The jaws that bite, the claws that catch! |
|||
Decoded text:THISWASTHEPOEMTHATALICEREADJABBERWOCKYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIMBLEINTHEWABEALLMIMSYWERETHEBOROGOVESANDTHEMOMERATHSOUTGRABEBEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCHBEWARETHEJUBJUBBIRDANDSHUNTHEFRUMIOUSBANDERSNATCHHETOOKHISVORPALSWORDINHANDLONGTIMETHEMANXOMEFOEHESOUGHTSORESTEDHEBYTHETUMTUMTREEANDSTOODAWHILEINTHOUGHTANDASINUFFISHTHOUGHTHESTOODTHEJABBERWOCKWITHEYESOFFLAMECAMEWHIFFLINGTHROUGHTHETULGEYWOODANDBURBLEDASITCAMEONETWOONETWOANDTHROUGHANDTHROUGHTHEVORPALBLADEWENTSNICKERSNACKHELEFTITDEADANDWITHITSHEADHEWENTGALUMPHINGBACKANDHASTTHOUSLAINTHEJABBERWOCKCOMETOMYARMSMYBEAMISHBOYOFRABJOUSDAYCALLOOHCALLAYHECHORTLEDINHISJOYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIMBLEINTHEWABEALLMIMSYWERETHEBOROGOVESANDTHEMOMERATHSOUTGRABEITSEEMSVERYPRETTYSHESAIDWHENSHEHADFINISHEDITBUTITSRATHERHARDTOUNDERSTAND |
|||
key: Vigenere Cipher |
|||
Code: WMCEEIKLGRPIFVMEUGXQPWQVIOIAVEYXUEKFKBTALVXTGAFXYEVKPAGY |
|||
Back: BEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCH |
|||
</pre> |
</pre> |
Latest revision as of 11:08, 16 February 2024
You are encouraged to solve this task according to the task description, using any language you may know.
Given some text you suspect has been encrypted with a Vigenère cipher, extract the key and plaintext. There are several methods for doing this. See the Wikipedia entry for more information. Use the following encrypted text:
MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA FWAML ZZRXJ EKAHV FASMU LVVUT TGK
Letter frequencies for English can be found here.
Specifics for this task:
- Take only the ciphertext as input. You can assume it's all capitalized and has no punctuation, but it might have whitespace.
- Assume the plaintext is written in English.
- Find and output the key.
- Use that key to decrypt and output the original plaintext. Maintaining the whitespace from the ciphertext is optional.
- The algorithm doesn't have to be perfect (which may not be possible) but it should work when given enough ciphertext. The example above is fairly long, and should be plenty for any algorithm.
11l
-V ascii_uppercase = Array(‘A’..‘Z’)
F vigenere_decrypt(target_freqs, input)
V nchars = :ascii_uppercase.len
V ordA = ‘A’.code
V sorted_targets = sorted(target_freqs)
F frequency(input)
V result = :ascii_uppercase.map(c -> (c, 0.0))
L(c) input
result[c - @ordA][1]++
R result
F correlation(input)
V result = 0.0
V freq = sorted(@frequency(input), key' a -> a[1])
L(f) freq
result += f[1] * @sorted_targets[L.index]
R result
V cleaned = input.uppercase().filter(c -> c.is_uppercase()).map(c -> c.code)
V best_len = 0
V best_corr = -100.0
L(i) 2 .< cleaned.len I/ 20
V pieces = [[Int]()] * i
L(c) cleaned
pieces[L.index % i].append(c)
V corr = -0.5 * i + sum(pieces.map(p -> @correlation(p)))
I corr > best_corr
best_len = i
best_corr = corr
I best_len == 0
R (‘Text is too short to analyze’, ‘’)
V pieces = [[Int]()] * best_len
L(c) cleaned
pieces[L.index % best_len].append(c)
V freqs = pieces.map(p -> @frequency(p))
V key = ‘’
L(fr_) freqs
V fr = sorted(fr_, key' a -> a[1], reverse' 1B)
V m = 0
V max_corr = 0.0
L(j) 0 .< nchars
V corr = 0.0
V c = ordA + j
L(frc) fr
V d = (frc[0].code - c + nchars) % nchars
corr += frc[1] * target_freqs[d]
I corr > max_corr
m = j
max_corr = corr
key ‘’= Char(code' m + ordA)
V r = (enumerate(cleaned).map((i, c) -> Char(code' (c - @key[i % @best_len].code + @nchars) % @nchars + @ordA)))
R (key, r.join(‘’))
V encoded = ‘
MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH
VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD
ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS
FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG
ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ
ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS
JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT
LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST
MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH
QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV
RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW
TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO
SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR
ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX
BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB
BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA
FWAML ZZRXJ EKAHV FASMU LVVUT TGK’
V english_frequences = [
0.08167, 0.01492, 0.02782, 0.04253, 0.12702, 0.02228, 0.02015,
0.06094, 0.06966, 0.00153, 0.00772, 0.04025, 0.02406, 0.06749,
0.07507, 0.01929, 0.00095, 0.05987, 0.06327, 0.09056, 0.02758,
0.00978, 0.02360, 0.00150, 0.01974, 0.00074]
V (key, decoded) = vigenere_decrypt(english_frequences, encoded)
print(‘Key: ’key)
print("\nText: "decoded)
- Output:
Key: THECHESHIRECAT Text: THISWASTHEPOEMTHATALICEREADJABBERWOCKYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIMBLEINTHEWABEALLMIMSYWERETHEBOROGOVE...
Ada
The program is not fully auto, but makes a small number of suggestions for the right key and plaintext.
with Ada.Text_IO;
procedure Vignere_Cryptanalysis is
subtype Letter is Character range 'A' .. 'Z';
function "+"(X, Y: Letter) return Letter is
begin
return Character'Val( ( (Character'Pos(X)-Character'Pos('A'))
+ (Character'Pos(Y)-Character'Pos('A')) ) mod 26
+ Character'Pos('A'));
end;
function "-"(X, Y: Letter) return Letter is
begin
return Character'Val( ( (Character'Pos(X)-Character'Pos('A'))
- (Character'Pos(Y)-Character'Pos('A')) ) mod 26
+ Character'Pos('A'));
end;
type Frequency_Array is array (Letter) of Float;
English: Frequency_Array :=
( 0.08167, 0.01492, 0.02782, 0.04253, 0.12702, 0.02228, 0.02015,
0.06094, 0.06966, 0.00153, 0.00772, 0.04025, 0.02406, 0.06749,
0.07507, 0.01929, 0.00095, 0.05987, 0.06327, 0.09056, 0.02758,
0.00978, 0.02360, 0.00150, 0.01974, 0.00074 );
function Get_Frequency(S: String) return Frequency_Array is
Result: Frequency_Array := (others => 0.0);
Offset: Float := 1.0/Float(S'Length);
begin
for I in S'Range loop
if S(I) in Letter then
Result(S(I)) := Result(S(I)) + Offset;
end if;
end loop;
return Result;
end Get_Frequency;
function Remove_Whitespace(S: String) return String is
begin
if S="" then
return "";
elsif S(S'First) in Letter then
return S(S'First) & Remove_Whitespace(S(S'First+1 .. S'Last));
else
return Remove_Whitespace(S(S'First+1 .. S'Last));
end if;
end Remove_Whitespace;
function Distance(A, B: Frequency_Array;
Offset: Character := 'A') return Float is
Result: Float := 0.0;
Diff: Float;
begin
for C in A'Range loop
Diff := A(C+Offset) - B(C);
Result := Result + (Diff * Diff);
end loop;
return Result;
end Distance;
function Find_Key(Cryptogram: String; Key_Length: Positive) return String is
function Find_Caesar_Key(S: String) return Letter is
Frequency: Frequency_Array := Get_Frequency(S);
Candidate: Letter := 'A'; -- a fake candidate
Candidate_Dist : Float := Distance(Frequency, English, 'A');
New_Dist: Float;
begin
for L in Letter range 'B' .. 'Z' loop
New_Dist := Distance(Frequency, English, L);
if New_Dist <= Candidate_Dist then
Candidate_Dist := New_Dist;
Candidate := L;
end if;
end loop;
return Candidate;
end Find_Caesar_Key;
function Get_Slide(S: String; Step: Positive) return String is
begin
if S'Length= 0 then
return "";
else
return S(S'First) & Get_Slide(S(S'First+Step .. S'Last), Step);
end if;
end Get_Slide;
Key: String(1 .. Key_Length);
S: String renames Cryptogram;
begin
for I in Key'Range loop
Key(I) := Find_Caesar_Key(Get_Slide(S(S'First+I-1 .. S'Last),
Key_Length));
end loop;
return Key;
end Find_Key;
function Key_Char(Key: String; Index: Positive) return Letter is
begin
if Index > Key'Last then
return Key_Char(Key, Index-Key'Last);
else
return Key(Index);
end if;
end Key_Char;
Ciphertext: String := Remove_Whitespace(
"MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH" &
"VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD" &
"ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS" &
"FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG" &
"ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ" &
"ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS" &
"JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT" &
"LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST" &
"MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH" &
"QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV" &
"RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW" &
"TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO" &
"SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR" &
"ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX" &
"BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB" &
"BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA" &
"FWAML ZZRXJ EKAHV FASMU LVVUT TGK");
Best_Plain: String := Ciphertext;
Best_Dist: Float := Distance(English, Get_Frequency(Best_Plain));
Best_Key: String := Ciphertext;
Best_Key_L: Natural := 0;
begin -- Vignere_Cryptanalysis
for I in 1 .. Ciphertext'Length/10 loop
declare
Key: String(1 .. I) := Find_Key(Ciphertext, I);
Plaintext: String(Ciphertext'Range);
begin
for I in Ciphertext'Range loop
Plaintext(I) := Ciphertext(I) - Key_Char(Key, I);
end loop;
if Distance(English, Get_Frequency(Plaintext)) < Best_Dist then
Best_Plain := Plaintext;
Best_Dist := Distance(English, Get_Frequency(Plaintext));
Best_Key(1 .. I) := Key;
Best_Key_L := I;
if Best_dist < 0.01 then
declare
use Ada.Text_IO;
begin
Put_Line("Key =" & Best_Key(1 .. Best_Key_L));
Put_Line("Distance = " & Float'Image(Best_Dist));
New_Line;
Put_Line("Plaintext =");
Put_Line(Best_Plain);
New_Line; New_Line;
end;
end if;
end if;
end;
end loop;
end Vignere_Cryptanalysis;
C
This finds the right key (I think, I didn't try to decode it after getting the key). The program is not fully auto, but by its output, the result is pretty obvious.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
const char *encoded =
"MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH"
"VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD"
"ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS"
"FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG"
"ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ"
"ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS"
"JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT"
"LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST"
"MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH"
"QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV"
"RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW"
"TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO"
"SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR"
"ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX"
"BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB"
"BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA"
"FWAML ZZRXJ EKAHV FASMU LVVUT TGK";
const double freq[] = {
0.08167, 0.01492, 0.02782, 0.04253, 0.12702, 0.02228, 0.02015,
0.06094, 0.06966, 0.00153, 0.00772, 0.04025, 0.02406, 0.06749,
0.07507, 0.01929, 0.00095, 0.05987, 0.06327, 0.09056, 0.02758,
0.00978, 0.02360, 0.00150, 0.01974, 0.00074
};
int best_match(const double *a, const double *b) {
double sum = 0, fit, d, best_fit = 1e100;
int i, rotate, best_rotate = 0;
for (i = 0; i < 26; i++)
sum += a[i];
for (rotate = 0; rotate < 26; rotate++) {
fit = 0;
for (i = 0; i < 26; i++) {
d = a[(i + rotate) % 26] / sum - b[i];
fit += d * d / b[i];
}
if (fit < best_fit) {
best_fit = fit;
best_rotate = rotate;
}
}
return best_rotate;
}
double freq_every_nth(const int *msg, int len, int interval, char *key) {
double sum, d, ret;
double out[26], accu[26] = {0};
int i, j, rot;
for (j = 0; j < interval; j++) {
for (i = 0; i < 26; i++)
out[i] = 0;
for (i = j; i < len; i += interval)
out[msg[i]]++;
key[j] = rot = best_match(out, freq);
key[j] += 'A';
for (i = 0; i < 26; i++)
accu[i] += out[(i + rot) % 26];
}
for (i = 0, sum = 0; i < 26; i++)
sum += accu[i];
for (i = 0, ret = 0; i < 26; i++) {
d = accu[i] / sum - freq[i];
ret += d * d / freq[i];
}
key[interval] = '\0';
return ret;
}
int main() {
int txt[strlen(encoded)];
int len = 0, j;
char key[100];
double fit, best_fit = 1e100;
for (j = 0; encoded[j] != '\0'; j++)
if (isupper(encoded[j]))
txt[len++] = encoded[j] - 'A';
for (j = 1; j < 30; j++) {
fit = freq_every_nth(txt, len, j, key);
printf("%f, key length: %2d, %s", fit, j, key);
if (fit < best_fit) {
best_fit = fit;
printf(" <--- best so far");
}
printf("\n");
}
return 0;
}
C++
Not guaranteed to give a 100% correct answer, but it works here. Requires C++0x.
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <algorithm>
#include <array>
using namespace std;
typedef array<pair<char, double>, 26> FreqArray;
class VigenereAnalyser
{
private:
array<double, 26> targets;
array<double, 26> sortedTargets;
FreqArray freq;
// Update the freqs array
FreqArray& frequency(const string& input)
{
for (char c = 'A'; c <= 'Z'; ++c)
freq[c - 'A'] = make_pair(c, 0);
for (size_t i = 0; i < input.size(); ++i)
freq[input[i] - 'A'].second++;
return freq;
}
double correlation(const string& input)
{
double result = 0.0;
frequency(input);
sort(freq.begin(), freq.end(), [](pair<char, double> u, pair<char, double> v)->bool
{ return u.second < v.second; });
for (size_t i = 0; i < 26; ++i)
result += freq[i].second * sortedTargets[i];
return result;
}
public:
VigenereAnalyser(const array<double, 26>& targetFreqs)
{
targets = targetFreqs;
sortedTargets = targets;
sort(sortedTargets.begin(), sortedTargets.end());
}
pair<string, string> analyze(string input)
{
string cleaned;
for (size_t i = 0; i < input.size(); ++i)
{
if (input[i] >= 'A' && input[i] <= 'Z')
cleaned += input[i];
else if (input[i] >= 'a' && input[i] <= 'z')
cleaned += input[i] + 'A' - 'a';
}
size_t bestLength = 0;
double bestCorr = -100.0;
// Assume that if there are less than 20 characters
// per column, the key's too long to guess
for (size_t i = 2; i < cleaned.size() / 20; ++i)
{
vector<string> pieces(i);
for (size_t j = 0; j < cleaned.size(); ++j)
pieces[j % i] += cleaned[j];
// The correlation increases artificially for smaller
// pieces/longer keys, so weigh against them a little
double corr = -0.5*i;
for (size_t j = 0; j < i; ++j)
corr += correlation(pieces[j]);
if (corr > bestCorr)
{
bestLength = i;
bestCorr = corr;
}
}
if (bestLength == 0)
return make_pair("Text is too short to analyze", "");
vector<string> pieces(bestLength);
for (size_t i = 0; i < cleaned.size(); ++i)
pieces[i % bestLength] += cleaned[i];
vector<FreqArray> freqs;
for (size_t i = 0; i < bestLength; ++i)
freqs.push_back(frequency(pieces[i]));
string key = "";
for (size_t i = 0; i < bestLength; ++i)
{
sort(freqs[i].begin(), freqs[i].end(), [](pair<char, double> u, pair<char, double> v)->bool
{ return u.second > v.second; });
size_t m = 0;
double mCorr = 0.0;
for (size_t j = 0; j < 26; ++j)
{
double corr = 0.0;
char c = 'A' + j;
for (size_t k = 0; k < 26; ++k)
{
int d = (freqs[i][k].first - c + 26) % 26;
corr += freqs[i][k].second * targets[d];
}
if (corr > mCorr)
{
m = j;
mCorr = corr;
}
}
key += m + 'A';
}
string result = "";
for (size_t i = 0; i < cleaned.size(); ++i)
result += (cleaned[i] - key[i % key.length()] + 26) % 26 + 'A';
return make_pair(result, key);
}
};
int main()
{
string input =
"MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH"
"VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD"
"ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS"
"FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG"
"ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ"
"ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS"
"JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT"
"LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST"
"MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH"
"QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV"
"RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW"
"TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO"
"SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR"
"ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX"
"BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB"
"BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA"
"FWAML ZZRXJ EKAHV FASMU LVVUT TGK";
array<double, 26> english = {
0.08167, 0.01492, 0.02782, 0.04253, 0.12702, 0.02228,
0.02015, 0.06094, 0.06966, 0.00153, 0.00772, 0.04025,
0.02406, 0.06749, 0.07507, 0.01929, 0.00095, 0.05987,
0.06327, 0.09056, 0.02758, 0.00978, 0.02360, 0.00150,
0.01974, 0.00074};
VigenereAnalyser va(english);
pair<string, string> output = va.analyze(input);
cout << "Key: " << output.second << endl << endl;
cout << "Text: " << output.first << endl;
}
D
import std.stdio, std.algorithm, std.typecons, std.string,
std.array, std.numeric, std.ascii;
string[2] vigenereDecrypt(in double[] targetFreqs, in string input) {
enum nAlpha = std.ascii.uppercase.length;
static double correlation(in string txt, in double[] sTargets)
pure nothrow /*@safe*/ @nogc {
uint[nAlpha] charCounts = 0;
foreach (immutable c; txt)
charCounts[c - 'A']++;
return charCounts[].sort().release.dotProduct(sTargets);
}
static frequency(in string txt) pure nothrow @safe {
auto freqs = new Tuple!(char,"c", uint,"d")[nAlpha];
foreach (immutable i, immutable c; std.ascii.uppercase)
freqs[i] = tuple(c, 0);
foreach (immutable c; txt)
freqs[c - 'A'].d++;
return freqs;
}
static string[2] decode(in string cleaned, in string key)
pure nothrow @safe {
assert(!key.empty);
string decoded;
foreach (immutable i, immutable c; cleaned)
decoded ~= (c - key[i % $] + nAlpha) % nAlpha + 'A';
return [key, decoded];
}
static size_t findBestLength(in string cleaned,
in double[] sTargets)
pure nothrow /*@safe*/ {
size_t bestLength;
double bestCorr = -100.0;
// Assume that if there are less than 20 characters
// per column, the key's too long to guess
foreach (immutable i; 2 .. cleaned.length / 20) {
auto pieces = new Appender!string[i];
foreach (immutable j, immutable c; cleaned)
pieces[j % i] ~= c;
// The correlation seems to increase for smaller
// pieces/longer keys, so weigh against them a little
double corr = -0.5 * i;
foreach (const p; pieces)
corr += correlation(p.data, sTargets);
if (corr > bestCorr) {
bestLength = i;
bestCorr = corr;
}
}
return bestLength;
}
static string findKey(in string cleaned, in size_t bestLength,
in double[] targetFreqs) pure nothrow @safe {
auto pieces = new string[bestLength];
foreach (immutable i, immutable c; cleaned)
pieces[i % bestLength] ~= c;
string key;
foreach (fr; pieces.map!frequency) {
fr.sort!q{ a.d > b.d };
size_t m;
double maxCorr = 0.0;
foreach (immutable j, immutable c; uppercase) {
double corr = 0.0;
foreach (immutable frc; fr) {
immutable di = (frc.c - c + nAlpha) % nAlpha;
corr += frc.d * targetFreqs[di];
}
if (corr > maxCorr) {
m = j;
maxCorr = corr;
}
}
key ~= m + 'A';
}
return key;
}
immutable cleaned = input.toUpper.removechars("^A-Z");
//immutable sortedTargets = targetFreqs.sorted;
immutable sortedTargets = targetFreqs.dup.sort().release.idup;
immutable bestLength = findBestLength(cleaned, sortedTargets);
if (bestLength == 0)
throw new Exception("Text is too short to analyze.");
immutable string key = findKey(cleaned, bestLength, targetFreqs);
return decode(cleaned, key);
}
void main() {
immutable encoded = "MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG
JSPXY ALUYM NSMYH VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF
WHTCQ KMLRD ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA
LWQIS FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG
ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ ILOVV
RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS JLAKI FHXUF
XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT LPRWM JAZPK LQUZA
ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST MTEOE PAPJH SMFNB YVQUZ
AALGA YDNMP AQOWT UHDBV TSMUE UIMVH QGVRW AEFSP EMPVE PKXZY WLKJA
GWALT VYYOB YIXOK IHPDS EVLEV RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY
IMAPX UOISK PVAGN MZHPW TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV
YOVDJ SOLXG TGRVO SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV
GJOKM SIFPR ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO
ZQDLX BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB
BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA FWAML
ZZRXJ EKAHV FASMU LVVUT TGK";
immutable englishFrequences = [0.08167, 0.01492, 0.02782, 0.04253,
0.12702, 0.02228, 0.02015, 0.06094, 0.06966, 0.00153, 0.00772,
0.04025, 0.02406, 0.06749, 0.07507, 0.01929, 0.00095, 0.05987,
0.06327, 0.09056, 0.02758, 0.00978, 0.02360, 0.00150, 0.01974,
0.00074];
immutable key_dec = vigenereDecrypt(englishFrequences, encoded);
writefln("Key: %s\n\nText: %s", key_dec[0], key_dec[1]);
}
- Output (cut):
Key: THECHESHIRECAT Text: THISWASTHEPOEMTHATALICEREADJABBERWOCKYTWASBRILLIGANDTHESLITHY...
Go
package main
import (
"fmt"
"strings"
)
var encoded =
"MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH" +
"VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD" +
"ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS" +
"FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG" +
"ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ" +
"ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS" +
"JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT" +
"LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST" +
"MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH" +
"QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV" +
"RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW" +
"TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO" +
"SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR" +
"ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX" +
"BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB" +
"BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA" +
"FWAML ZZRXJ EKAHV FASMU LVVUT TGK"
var freq = [26]float64{
0.08167, 0.01492, 0.02782, 0.04253, 0.12702, 0.02228, 0.02015,
0.06094, 0.06966, 0.00153, 0.00772, 0.04025, 0.02406, 0.06749,
0.07507, 0.01929, 0.00095, 0.05987, 0.06327, 0.09056, 0.02758,
0.00978, 0.02360, 0.00150, 0.01974, 0.00074,
}
func sum(a []float64) (sum float64) {
for _, f := range a {
sum += f
}
return
}
func bestMatch(a []float64) int {
sum := sum(a)
bestFit, bestRotate := 1e100, 0
for rotate := 0; rotate < 26; rotate++ {
fit := 0.0
for i := 0; i < 26; i++ {
d := a[(i+rotate)%26]/sum - freq[i]
fit += d * d / freq[i]
}
if fit < bestFit {
bestFit, bestRotate = fit, rotate
}
}
return bestRotate
}
func freqEveryNth(msg []int, key []byte) float64 {
l := len(msg)
interval := len(key)
out := make([]float64, 26)
accu := make([]float64, 26)
for j := 0; j < interval; j++ {
for k := 0; k < 26; k++ {
out[k] = 0.0
}
for i := j; i < l; i += interval {
out[msg[i]]++
}
rot := bestMatch(out)
key[j] = byte(rot + 65)
for i := 0; i < 26; i++ {
accu[i] += out[(i+rot)%26]
}
}
sum := sum(accu)
ret := 0.0
for i := 0; i < 26; i++ {
d := accu[i]/sum - freq[i]
ret += d * d / freq[i]
}
return ret
}
func decrypt(text, key string) string {
var sb strings.Builder
ki := 0
for _, c := range text {
if c < 'A' || c > 'Z' {
continue
}
ci := (c - rune(key[ki]) + 26) % 26
sb.WriteRune(ci + 65)
ki = (ki + 1) % len(key)
}
return sb.String()
}
func main() {
enc := strings.Replace(encoded, " ", "", -1)
txt := make([]int, len(enc))
for i := 0; i < len(txt); i++ {
txt[i] = int(enc[i] - 'A')
}
bestFit, bestKey := 1e100, ""
fmt.Println(" Fit Length Key")
for j := 1; j <= 26; j++ {
key := make([]byte, j)
fit := freqEveryNth(txt, key)
sKey := string(key)
fmt.Printf("%f %2d %s", fit, j, sKey)
if fit < bestFit {
bestFit, bestKey = fit, sKey
fmt.Print(" <--- best so far")
}
fmt.Println()
}
fmt.Println("\nBest key :", bestKey)
fmt.Printf("\nDecrypted text:\n%s\n", decrypt(enc, bestKey))
}
- Output:
Note: carriage returns inserted into decrypted text after every 80 characters to make it more readable.
Fit Length Key 2.984348 1 E <--- best so far 2.483684 2 EC <--- best so far 2.642487 3 TEE 1.976651 4 THEC <--- best so far 2.356881 5 EEEPU 2.203129 6 TCECEC 1.051163 7 THECSAS <--- best so far 1.645763 8 TJQGAHET 2.001380 9 VEIZSEGNT 1.824476 10 ECEGAWQTDS 1.623083 11 TNLUSRXPTAJ 1.253527 12 XLECTHQGTHEC 1.399037 13 LJJTDGFNOTENR 0.152370 14 THECHESHIRECAT <--- best so far 1.533951 15 JNTOOEEXFTGQTNH 1.068182 16 TJTSAEETEXHPXHNE 1.034093 17 AZRAXUHEJLREEXIEE 1.443345 18 VNIZQPALEPTSXSEXUC 1.090977 19 FUCAITCSLVTEZDUDEHS 0.979868 20 EQXGAHWTTQECEWUGXHPI 0.789410 21 HVRCSAFTHEBDLSTAERSES 0.881380 22 TVIJTCIGKAQPELECRXPTNC 0.952456 23 KKEQXGPWTCQEELIEHXUWASV 0.715968 24 ELAIXHQTTIEDXJETTNTGAEPC 0.891258 25 OTJUUEGERDNQTUQEAGWUTIEOA 0.852784 26 IGITEGECAGAVUNLJAHASAVTETW Best key : THECHESHIRECAT Decrypted text: THISWASTHEPOEMTHATALICEREADJABBERWOCKYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIMB LEINTHEWABEALLMIMSYWERETHEBOROGOVESANDTHEMOMERATHSOUTGRABEBEWARETHEJABBERWOCKMYS ONTHEJAWSTHATBITETHECLAWSTHATCATCHBEWARETHEJUBJUBBIRDANDSHUNTHEFRUMIOUSBANDERSNA TCHHETOOKHISVORPALSWORDINHANDLONGTIMETHEMANXOMEFOEHESOUGHTSORESTEDHEBYTHETUMTUMT REEANDSTOODAWHILEINTHOUGHTANDASINUFFISHTHOUGHTHESTOODTHEJABBERWOCKWITHEYESOFFLAM ECAMEWHIFFLINGTHROUGHTHETULGEYWOODANDBURBLEDASITCAMEONETWOONETWOANDTHROUGHANDTHR OUGHTHEVORPALBLADEWENTSNICKERSNACKHELEFTITDEADANDWITHITSHEADHEWENTGALUMPHINGBACK ANDHASTTHOUSLAINTHEJABBERWOCKCOMETOMYARMSMYBEAMISHBOYOFRABJOUSDAYCALLOOHCALLAYHE CHORTLEDINHISJOYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIMBLEINTHEWABEALLMIMSYWER ETHEBOROGOVESANDTHEMOMERATHSOUTGRABEITSEEMSVERYPRETTYSHESAIDWHENSHEHADFINISHEDIT BUTITSRATHERHARDTOUNDERSTAND
Haskell
{-# LANGUAGE TupleSections #-}
import Data.List(transpose, nub, sort, maximumBy)
import Data.Ord (comparing)
import Data.Char (ord)
import Data.Map (Map, fromListWith, toList, findWithDefault)
average :: Fractional a => [a] -> a
average as = sum as / fromIntegral (length as)
-- Create a map from each entry in list to the number of occurrences of
-- that entry in the list.
countEntries :: Ord a => [a] -> Map a Int
countEntries = fromListWith (+) . fmap (,1)
-- Break a string up into substrings of n chars.
breakup :: Int -> [a] -> [[a]]
breakup _ [] = []
breakup n as =
let (h, r) = splitAt n as
in h:breakup n r
-- Dole out elements of a string over a n element distribution.
distribute :: [a] -> Int -> [[a]]
distribute as n = transpose $ breakup n as
-- The probability that members of a pair of characters taken randomly
-- from a given string are equal.
coincidence :: (Ord a, Fractional b) => [a] -> b
coincidence str =
let charCounts = snd <$> toList (countEntries str)
strln = length str
d = fromIntegral $ strln * (strln - 1)
n = fromIntegral $ sum $ fmap (\cc -> cc * (cc-1)) charCounts
in n / d
-- Use the average probablity of coincidence for all the members of
-- a distribution to rate the distribution - the higher the better.
-- The correlation increases artificially for smaller
-- pieces/longer keys, so weigh against them a little
rate :: (Ord a, Fractional b) => [[a]] -> b
rate d = average (fmap coincidence d) - fromIntegral (length d) / 3000.0
-- Multiply elements of lists together and add up the results.
dot :: Num a => [a] -> [a] -> a
dot v0 v1 = sum $ zipWith (*) v0 v1
-- Given two lists of floats, rotate one of them by the number of
-- characters indicated by letter and then 'dot' them together.
rotateAndDot :: Num a => [a] -> [a] -> Char -> a
rotateAndDot v0 v1 letter = dot v0 (drop (ord letter - ord 'A') (cycle v1))
-- Find decoding offset that results in best match
-- between actual char frequencies and expected frequencies.
getKeyChar :: RealFrac a => [a] -> String -> Char
getKeyChar expected sample =
let charCounts = countEntries sample
countInSample c = findWithDefault 0 c charCounts
actual = fmap (fromIntegral . countInSample) ['A'..'Z']
in maximumBy (comparing $ rotateAndDot expected actual) ['A'..'Z']
main = do
let cr = filter (/=' ') crypt
-- Assume that if there are less than 20 characters
-- per column, the key's too long to guess
distributions = fmap (distribute cr) [1..length cr `div` 20]
bestDistribution = maximumBy (comparing rate) distributions
key = fmap (getKeyChar englishFrequencies) bestDistribution
alphaSum a b = ['A'..'Z'] !! ((ord b - ord a) `mod` 26)
mapM_ putStrLn ["Key: " ++ key, "Decrypted Text: " ++ zipWith alphaSum (cycle key) cr]
englishFrequencies =
[ 0.08167, 0.01492, 0.02782, 0.04253,
0.12702, 0.02228, 0.02015, 0.06094,
0.06966, 0.00153, 0.00772, 0.04025,
0.02406, 0.06749, 0.07507, 0.01929,
0.00095, 0.05987, 0.06327, 0.09056,
0.02758, 0.00978, 0.02360, 0.00150,
0.01974, 0.00074 ]
crypt = "\
\MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH\
\VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD\
\ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS\
\FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG\
\ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ\
\ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS\
\JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT\
\LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST\
\MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH\
\QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV\
\RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW\
\TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO\
\SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR\
\ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX\
\BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB\
\BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA\
\FWAML ZZRXJ EKAHV FASMU LVVUT TGK\
\"
- Output:
Key: THECHESHIRECAT Decrypted Text: THISWASTHEPOEMTHATALICEREADJABBERWOCKYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIMBLEINTHEWABEALLMIMSYWERETHEBOROGOVESANDTHEMOMERATHSOUTGRABEBEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCHBEWARETHEJUBJUBBIRDANDSHUNTHEFRUMIOUSBANDERSNATCHHETOOKHISVORPALSWORDINHANDLONGTIMETHEMANXOMEFOEHESOUGHTSORESTEDHEBYTHETUMTUMTREEANDSTOODAWHILEINTHOUGHTANDASINUFFISHTHOUGHTHESTOODTHEJABBERWOCKWITHEYESOFFLAMECAMEWHIFFLINGTHROUGHTHETULGEYWOODANDBURBLEDASITCAMEONETWOONETWOANDTHROUGHANDTHROUGHTHEVORPALBLADEWENTSNICKERSNACKHELEFTITDEADANDWITHITSHEADHEWENTGALUMPHINGBACKANDHASTTHOUSLAINTHEJABBERWOCKCOMETOMYARMSMYBEAMISHBOYOFRABJOUSDAYCALLOOHCALLAYHECHORTLEDINHISJOYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIMBLEINTHEWABEALLMIMSYWERETHEBOROGOVESANDTHEMOMERATHSOUTGRABEITSEEMSVERYPRETTYSHESAIDWHENSHEHADFINISHEDITBUTITSRATHERHARDTOUNDERSTAND
J
Implementation:
NB. https://en.wikipedia.org/wiki/Kasiski_examination
kasiski=: {{
grams=. ({: #"1~1 < ;@{.)|:(#/.~;"0~.) g=. 3 <\ y
deltas=. ;grams (2 -~/\ I.@E.)L:0 enc
{:,{.\:~(#/.~,.~.)1 -.~,+./~ deltas
}}
NB. https://en.wikipedia.org/wiki/Letter_frequency
AZ=: 8 u: 65+i.26
lfreq=: 0.01*do{{)n
8.2 1.5 2.8 4.3 13 2.2 2 6.1 7 0.15
0.77 4 2.4 6.7 7.5 1.9 0.095 6 6.3 9.1
2.8 0.98 2.4 0.15 2 0.074
}}-.LF
caesarkey=: {{
freqs=. (<:#/.~AZ,y)%#y=. y ([-.-.) AZ
AZ{~(i. <./)lfreq +/&.:*:@:-"1 (i.26)|."0 1 freqs
}}
vigenerekey=: {{ caesarkey"1|:(-kasiski y) ]\y }}
uncaesar=: {{ 26&|@-&(AZ i.x)&.(AZ&i.) y }}"0 1
unvigenere=: {{ ' '-.~,x uncaesar"0 1&.|:(-#x) ]\y }}
Here, kasiski finds all 3-grams (sequences of three adjacent letters) which appear more than once, finds all of the distances between nearest pairs of these sequences, and then further pairs each of these distances with all other distances, finding the greatest common divisor of those distance pairs. Finally, these LCDs are ordered by how many times they appear and the most frequent LCD is taken as the kasiski result.
uncaesar works by finding the frequency of occurrence of each letter of the alphabet (in alphabetical order), and then each of the 26 rotations of that sequence are compared with a text frequency alphabet (obtained from a wikipedia table). The rotation with the least root-mean-square sum of differences is chosen as the correct location, and its index is reported as a letter of the alphabet (0=A, 1=B, etc.)
(And, the length provided by kasiski is used to break out the sequences to be analyzed by uncaesar...)
Task example:
enc=: {{)n
MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH
VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD
ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS
FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG
ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ
ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS
JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT
LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST
MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH
QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV
RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW
TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO
SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR
ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX
BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB
BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA
FWAML ZZRXJ EKAHV FASMU LVVUT TGK
}}-.LF,' '
vigenerekey enc
THECHESHIRECAT
_80]\'THECHESHIRECAT' unvigenere enc
THISWASTHEPOEMTHATALICEREADJABBERWOCKYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIMB
LEINTHEWABEALLMIMSYWERETHEBOROGOVESANDTHEMOMERATHSOUTGRABEBEWARETHEJABBERWOCKMYS
ONTHEJAWSTHATBITETHECLAWSTHATCATCHBEWARETHEJUBJUBBIRDANDSHUNTHEFRUMIOUSBANDERSNA
TCHHETOOKHISVORPALSWORDINHANDLONGTIMETHEMANXOMEFOEHESOUGHTSORESTEDHEBYTHETUMTUMT
REEANDSTOODAWHILEINTHOUGHTANDASINUFFISHTHOUGHTHESTOODTHEJABBERWOCKWITHEYESOFFLAM
ECAMEWHIFFLINGTHROUGHTHETULGEYWOODANDBURBLEDASITCAMEONETWOONETWOANDTHROUGHANDTHR
OUGHTHEVORPALBLADEWENTSNICKERSNACKHELEFTITDEADANDWITHITSHEADHEWENTGALUMPHINGBACK
ANDHASTTHOUSLAINTHEJABBERWOCKCOMETOMYARMSMYBEAMISHBOYOFRABJOUSDAYCALLOOHCALLAYHE
CHORTLEDINHISJOYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIMBLEINTHEWABEALLMIMSYWER
ETHEBOROGOVESANDTHEMOMERATHSOUTGRABEITSEEMSVERYPRETTYSHESAIDWHENSHEHADFINISHEDIT
BUTITSRATHERHARDTOUNDERSTANDWYTWITSJWYAH
As an aside, note that we could go directly from encrypted text to decrypted text, without showing the key. For example, using:
decaesar=: {{
freqs=. (<:#/.~AZ,y)%#y=. y ([-.-.) AZ
ndx=. (i. <./)lfreq +/&.:*:@:-"1 (i.26)|."0 1 freqs
26&|@-&ndx&.(AZ&i.) y
}}
devigenere=: {{ ' '-.~,decaesar"1&.|:(-kasiski y) ]\y }}
That said, it's also worth noting that noise issues mean that if this were to be used in practical contexts the approach should instead be to expose more intermediate results, rather than less, with a special focus on the representations of frequency distributions (here, we're always picking the first alternative, but it's vaguely plausible that a different alternative might actually be useful in some cases).
Java
public class Vig{
static String encodedMessage =
"MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA FWAML ZZRXJ EKAHV FASMU LVVUT TGK";
final static double freq[] = {
0.08167, 0.01492, 0.02782, 0.04253, 0.12702, 0.02228, 0.02015,
0.06094, 0.06966, 0.00153, 0.00772, 0.04025, 0.02406, 0.06749,
0.07507, 0.01929, 0.00095, 0.05987, 0.06327, 0.09056, 0.02758,
0.00978, 0.02360, 0.00150, 0.01974, 0.00074
};
public static void main(String[] args) {
int lenghtOfEncodedMessage = encodedMessage.length();
char[] encoded = new char [lenghtOfEncodedMessage] ;
char[] key = new char [lenghtOfEncodedMessage] ;
encodedMessage.getChars(0, lenghtOfEncodedMessage, encoded, 0);
int txt[] = new int[lenghtOfEncodedMessage];
int len = 0, j;
double fit, best_fit = 1e100;
for (j = 0; j < lenghtOfEncodedMessage; j++)
if (Character.isUpperCase(encoded[j]))
txt[len++] = encoded[j] - 'A';
for (j = 1; j < 30; j++) {
fit = freq_every_nth(txt, len, j, key);
System.out.printf("%f, key length: %2d ", fit, j);
System.out.print(key);
if (fit < best_fit) {
best_fit = fit;
System.out.print(" <--- best so far");
}
System.out.print("\n");
}
}
static String decrypt(String text, final String key) {
String res = "";
text = text.toUpperCase();
for (int i = 0, j = 0; i < text.length(); i++) {
char c = text.charAt(i);
if (c < 'A' || c > 'Z') continue;
res += (char)((c - key.charAt(j) + 26) % 26 + 'A');
j = ++j % key.length();
}
return res;
}
static int best_match(final double []a, final double []b) {
double sum = 0, fit, d, best_fit = 1e100;
int i, rotate, best_rotate = 0;
for (i = 0; i < 26; i++)
sum += a[i];
for (rotate = 0; rotate < 26; rotate++) {
fit = 0;
for (i = 0; i < 26; i++) {
d = a[(i + rotate) % 26] / sum - b[i];
fit += d * d / b[i];
}
if (fit < best_fit) {
best_fit = fit;
best_rotate = rotate;
}
}
return best_rotate;
}
static double freq_every_nth(final int []msg, int len, int interval, char[] key) {
double sum, d, ret;
double [] accu = new double [26];
double [] out = new double [26];
int i, j, rot;
for (j = 0; j < interval; j++) {
for (i = 0; i < 26; i++)
out[i] = 0;
for (i = j; i < len; i += interval)
out[msg[i]]++;
rot = best_match(out, freq);
try{
key[j] = (char)(rot + 'A');
} catch (Exception e) {
System.out.print(e.getMessage());
}
for (i = 0; i < 26; i++)
accu[i] += out[(i + rot) % 26];
}
for (i = 0, sum = 0; i < 26; i++)
sum += accu[i];
for (i = 0, ret = 0; i < 26; i++) {
d = accu[i] / sum - freq[i];
ret += d * d / freq[i];
}
key[interval] = '\0';
return ret;
}
}
Julia
# ciphertext block {{{1
const ciphertext = filter(isalpha, """
MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH
VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD
ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS
FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG
ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ
ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS
JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT
LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST
MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH
QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV
RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW
TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO
SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR
ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX
BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB
BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA
FWAML ZZRXJ EKAHV FASMU LVVUT TGK
""")
# }}}
# character frequencies {{{1
const letters = Dict{Char, Float32}(
'E' => 12.702,
'T' => 9.056,
'A' => 8.167,
'O' => 7.507,
'I' => 6.966,
'N' => 6.749,
'S' => 6.327,
'H' => 6.094,
'R' => 5.987,
'D' => 4.253,
'L' => 4.025,
'C' => 2.782,
'U' => 2.758,
'M' => 2.406,
'W' => 2.361,
'F' => 2.228,
'G' => 2.015,
'Y' => 1.974,
'P' => 1.929,
'B' => 1.492,
'V' => 0.978,
'K' => 0.772,
'J' => 0.153,
'X' => 0.150,
'Q' => 0.095,
'Z' => 0.074)
const digraphs = Dict{AbstractString, Float32}(
"TH" => 15.2,
"HE" => 12.8,
"IN" => 9.4,
"ER" => 9.4,
"AN" => 8.2,
"RE" => 6.8,
"ND" => 6.3,
"AT" => 5.9,
"ON" => 5.7,
"NT" => 5.6,
"HA" => 5.6,
"ES" => 5.6,
"ST" => 5.5,
"EN" => 5.5,
"ED" => 5.3,
"TO" => 5.2,
"IT" => 5.0,
"OU" => 5.0,
"EA" => 4.7,
"HI" => 4.6,
"IS" => 4.6,
"OR" => 4.3,
"TI" => 3.4,
"AS" => 3.3,
"TE" => 2.7,
"ET" => 1.9,
"NG" => 1.8,
"OF" => 1.6,
"AL" => 0.9,
"DE" => 0.9,
"SE" => 0.8,
"LE" => 0.8,
"SA" => 0.6,
"SI" => 0.5,
"AR" => 0.4,
"VE" => 0.4,
"RA" => 0.4,
"LD" => 0.2,
"UR" => 0.2)
const trigraphs = Dict{AbstractString, Float32}(
"THE" => 18.1,
"AND" => 7.3,
"ING" => 7.2,
"ION" => 4.2,
"ENT" => 4.2,
"HER" => 3.6,
"FOR" => 3.4,
"THA" => 3.3,
"NTH" => 3.3,
"INT" => 3.2,
"TIO" => 3.1,
"ERE" => 3.1,
"TER" => 3.0,
"EST" => 2.8,
"ERS" => 2.8,
"HAT" => 2.6,
"ATI" => 2.6,
"ATE" => 2.5,
"ALL" => 2.5,
"VER" => 2.4,
"HIS" => 2.4,
"HES" => 2.4,
"ETH" => 2.4,
"OFT" => 2.2,
"STH" => 2.1,
"RES" => 2.1,
"OTH" => 2.1,
"ITH" => 2.1,
"FTH" => 2.1,
"ONT" => 2.0)
# 1}}}
function decrypt(enc::ASCIIString, key::ASCIIString)
const enclen = length(enc)
const keylen = length(key)
if keylen < enclen
key = (key^(div(enclen - keylen, keylen) + 2))[1:enclen]
end
msg = Array(Char, enclen)
for i=1:enclen
msg[i] = Char((Int(enc[i]) - Int(key[i]) + 26) % 26 + 65)
end
msg::Array{Char, 1}
end
function cryptanalyze(enc::ASCIIString; maxkeylen::Integer = 20)
const enclen = length(enc)
maxkey = ""
maxdec = ""
maxscore = 0.0
for keylen=1:maxkeylen
key = Array(Char, keylen)
idx = filter(x -> x % keylen == 0, 1:enclen) - keylen + 1
for i=1:keylen
maxsubscore = 0.0
for j='A':'Z'
subscore = 0.0
for k in decrypt(enc[idx], ascii(string(j)))
subscore += get(letters, k, 0.0)
end
if subscore > maxsubscore
maxsubscore = subscore
key[i] = j
end
end
idx += 1
end
key = join(key)
const dec = decrypt(enc, key)
score = 0.0
for i in dec
score += get(letters, i, 0.0)
end
for i=1:enclen - 2
const digraph = string(dec[i], dec[i + 1])
const trigraph = string(dec[i], dec[i + 1], dec[i + 2])
if haskey(digraphs, digraph)
score += 2 * get(digraphs, digraph, 0.0)
end
if haskey(trigraphs, trigraph)
score += 3 * get(trigraphs, trigraph, 0.0)
end
end
if score > maxscore
maxscore = score
maxkey = key
maxdec = dec
end
end
(maxkey, join(maxdec))::Tuple{ASCIIString, ASCIIString}
end
key, dec = cryptanalyze(ciphertext)
println("key: ", key, "\n\n", dec)
# post-compilation profiling run
gc()
t = @elapsed cryptanalyze(ciphertext)
println("\nelapsed time: ", t, " seconds")
- Output:
key: THECHESHIRECAT THISWASTHEPOEMTHATALICEREADJABBERWOCKYTWASBRILLIGANDTHESLITHY... elapsed time: 0.042894211 seconds
Kotlin
This is a reasonably faithful translation of the C entry though I've restricted the key lengths examined to 26 to automatically produce the correct key and hence decrypted text. This is because the C entry examines key lengths up to 29 and a value of 28 gives a slightly better fit even though the key produced (THECHESCIRECATTHECHESHIRECAT) and resulting text don't make as much sense and so would be rejected if one were examining the candidate keys manually.
// version 1.1.3
val encoded =
"MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH" +
"VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD" +
"ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS" +
"FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG" +
"ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ" +
"ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS" +
"JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT" +
"LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST" +
"MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH" +
"QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV" +
"RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW" +
"TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO" +
"SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR" +
"ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX" +
"BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB" +
"BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA" +
"FWAML ZZRXJ EKAHV FASMU LVVUT TGK"
val freq = doubleArrayOf(
0.08167, 0.01492, 0.02782, 0.04253, 0.12702, 0.02228, 0.02015,
0.06094, 0.06966, 0.00153, 0.00772, 0.04025, 0.02406, 0.06749,
0.07507, 0.01929, 0.00095, 0.05987, 0.06327, 0.09056, 0.02758,
0.00978, 0.02360, 0.00150, 0.01974, 0.00074
)
fun bestMatch(a: DoubleArray): Int {
val sum = a.sum()
var bestFit = 1e100
var bestRotate = 0
for (rotate in 0..25) {
var fit = 0.0
for (i in 0..25) {
val d = a[(i + rotate) % 26] / sum - freq[i]
fit += d * d / freq[i]
}
if (fit < bestFit) {
bestFit = fit
bestRotate = rotate
}
}
return bestRotate
}
fun freqEveryNth(msg: IntArray, key: CharArray): Double {
val len = msg.size
val interval = key.size
val out = DoubleArray(26)
val accu = DoubleArray(26)
for (j in 0 until interval) {
out.fill(0.0)
for (i in j until len step interval) out[msg[i]]++
val rot = bestMatch(out)
key[j] = (rot + 65).toChar()
for (i in 0..25) accu[i] += out[(i + rot) % 26]
}
val sum = accu.sum()
var ret = 0.0
for (i in 0..25) {
val d = accu[i] / sum - freq[i]
ret += d * d / freq[i]
}
return ret
}
fun decrypt(text: String, key: String): String {
val sb = StringBuilder()
var ki = 0
for (c in text) {
if (c !in 'A'..'Z') continue
val ci = (c.toInt() - key[ki].toInt() + 26) % 26
sb.append((ci + 65).toChar())
ki = (ki + 1) % key.length
}
return sb.toString()
}
fun main(args: Array<String>) {
val enc = encoded.replace(" ", "")
val txt = IntArray(enc.length) { enc[it] - 'A' }
var bestFit = 1e100
var bestKey = ""
val f = "%f %2d %s"
println(" Fit Length Key")
for (j in 1..26) {
val key = CharArray(j)
val fit = freqEveryNth(txt, key)
val sKey = key.joinToString("")
print(f.format(fit, j, sKey))
if (fit < bestFit) {
bestFit = fit
bestKey = sKey
print(" <--- best so far")
}
println()
}
println()
println("Best key : $bestKey")
println("\nDecrypted text:\n${decrypt(enc, bestKey)}")
}
- Output:
Fit Length Key 2.984348 1 E <--- best so far 2.483684 2 EC <--- best so far 2.642487 3 TEE 1.976651 4 THEC <--- best so far 2.356881 5 EEEPU 2.203129 6 TCECEC 1.051163 7 THECSAS <--- best so far 1.645763 8 TJQGAHET 2.001380 9 VEIZSEGNT 1.824476 10 ECEGAWQTDS 1.623083 11 TNLUSRXPTAJ 1.253527 12 XLECTHQGTHEC 1.399037 13 LJJTDGFNOTENR 0.152370 14 THECHESHIRECAT <--- best so far 1.533951 15 JNTOOEEXFTGQTNH 1.068182 16 TJTSAEETEXHPXHNE 1.034093 17 AZRAXUHEJLREEXIEE 1.443345 18 VNIZQPALEPTSXSEXUC 1.090977 19 FUCAITCSLVTEZDUDEHS 0.979868 20 EQXGAHWTTQECEWUGXHPI 0.789410 21 HVRCSAFTHEBDLSTAERSES 0.881380 22 TVIJTCIGKAQPELECRXPTNC 0.952456 23 KKEQXGPWTCQEELIEHXUWASV 0.715968 24 ELAIXHQTTIEDXJETTNTGAEPC 0.891258 25 OTJUUEGERDNQTUQEAGWUTIEOA 0.852784 26 IGITEGECAGAVUNLJAHASAVTETW Best key : THECHESHIRECAT Decrypted text: THISWASTHEPOEMTHATALICEREADJABBERWOCKYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIMBLEINTHEWABEALLMIMSYWERETHEBOROGOVESANDTHEMOMERATHSOUTGRABEBEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCHBEWARETHEJUBJUBBIRDANDSHUNTHEFRUMIOUSBANDERSNATCHHETOOKHISVORPALSWORDINHANDLONGTIMETHEMANXOMEFOEHESOUGHTSORESTEDHEBYTHETUMTUMTREEANDSTOODAWHILEINTHOUGHTANDASINUFFISHTHOUGHTHESTOODTHEJABBERWOCKWITHEYESOFFLAMECAMEWHIFFLINGTHROUGHTHETULGEYWOODANDBURBLEDASITCAMEONETWOONETWOANDTHROUGHANDTHROUGHTHEVORPALBLADEWENTSNICKERSNACKHELEFTITDEADANDWITHITSHEADHEWENTGALUMPHINGBACKANDHASTTHOUSLAINTHEJABBERWOCKCOMETOMYARMSMYBEAMISHBOYOFRABJOUSDAYCALLOOHCALLAYHECHORTLEDINHISJOYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIMBLEINTHEWABEALLMIMSYWERETHEBOROGOVESANDTHEMOMERATHSOUTGRABEITSEEMSVERYPRETTYSHESAIDWHENSHEHADFINISHEDITBUTITSRATHERHARDTOUNDERSTAND
Nim
This is a translation of Julia algorithm with some ideas from Phix translation.
import sequtils, strutils, sugar, tables, times
const
CipherText = """MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH
VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD
ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS
FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG
ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ
ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS
JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT
LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST
MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH
QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV
RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW
TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO
SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR
ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX
BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB
BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA
FWAML ZZRXJ EKAHV FASMU LVVUT TGK""".splitWhitespace.join()
FreqLetters = {'E': 12.702, 'T': 9.056, 'A': 8.167, 'O': 7.507,
'I': 6.966, 'N': 6.749, 'S': 6.327, 'H': 6.094,
'R': 5.987, 'D': 4.253, 'L': 4.025, 'C': 2.782,
'U': 2.758, 'M': 2.406, 'W': 2.361, 'F': 2.228,
'G': 2.015, 'Y': 1.974, 'P': 1.929, 'B': 1.492,
'V': 0.978, 'K': 0.772, 'J': 0.153, 'X': 0.150,
'Q': 0.095, 'Z': 0.074}.toTable
FreqDigraphs = {"TH": 15.2, "HE": 12.8, "IN": 9.4, "ER": 9.4,
"AN": 8.2, "RE": 6.8, "ND": 6.3, "AT": 5.9,
"ON": 5.7, "NT": 5.6, "HA": 5.6, "ES": 5.6,
"ST": 5.5, "EN": 5.5, "ED": 5.3, "TO": 5.2,
"IT": 5.0, "OU": 5.0, "EA": 4.7, "HI": 4.6,
"IS": 4.6, "OR": 4.3, "TI": 3.4, "AS": 3.3,
"TE": 2.7, "ET": 1.9, "NG": 1.8, "OF": 1.6,
"AL": 0.9, "DE": 0.9, "SE": 0.8, "LE": 0.8,
"SA": 0.6, "SI": 0.5, "AR": 0.4, "VE": 0.4,
"RA": 0.4, "LD": 0.2, "UR": 0.2}.toTable
FreqTrigraphs = {"THE": 18.1, "AND": 7.3, "ING": 7.2, "ION": 4.2,
"ENT": 4.2, "HER": 3.6, "FOR": 3.4, "THA": 3.3,
"NTH": 3.3, "INT": 3.2, "TIO": 3.1, "ERE": 3.1,
"TER": 3.0, "EST": 2.8, "ERS": 2.8, "HAT": 2.6,
"ATI": 2.6, "ATE": 2.5, "ALL": 2.5, "VER": 2.4,
"HIS": 2.4, "HES": 2.4, "ETH": 2.4, "OFT": 2.2,
"STH": 2.1, "RES": 2.1, "OTH": 2.1, "ITH": 2.1,
"FTH": 2.1, "ONT": 2.0}.toTable
func decrypt(enc, key: string): string =
let encLen = enc.len
let keyLen = key.len
result.setLen(encLen)
var k = 0
for i in 0..<encLen:
result[i] = chr((ord(enc[i]) - ord(key[k]) + 26) mod 26 + ord('A'))
k = (k + 1) mod keyLen
func cryptanalyze(enc: string; maxKeyLen = 20): tuple[maxKey, maxDec: string] =
let encLen = enc.len
var maxScore = 0.0
for keyLen in 1..maxKeyLen:
var key = newString(keyLen)
var idx = collect(newSeq):
for i in 1..encLen:
if i mod keyLen == 0:
i - keyLen
for i in 0..<keyLen:
var maxSubscore = 0.0
for j in 'A'..'Z':
var subscore = 0.0
let encidx = idx.mapIt(enc[it]).join()
for k in decrypt(encidx, $j):
subscore += FreqLetters[k]
if subscore > maxSubscore:
maxSubscore = subscore
key[i] = j
for item in idx.mitems: inc item
let dec = decrypt(enc, key)
var score = 0.0
for i in dec:
score += FreqLetters[i]
for i in 0..(encLen - 3):
let digraph = dec[i..(i+1)]
let trigraph = dec[i..(i+2)]
score += 2 * FreqDigraphs.getOrDefault(digraph)
score += 3 * FreqTrigraphs.getOrDefault(trigraph)
if score > maxScore:
maxScore = score
result.maxKey = key
result.maxDec = dec
let t0 = cpuTime()
let (key, dec) = CipherText.cryptanalyze()
echo "key: ", key, '\n'
echo dec, '\n'
echo "Elapsed time: ", (cpuTime() - t0).formatFloat(ffDecimal, precision = 3), " s"
- Output:
key: THECHESHIRECAT THISWASTHEPOEMTHATALICEREADJABBERWOCKYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIMBLEINTHEWABEALLMIMSYWERETHEBOROGOVESANDTHEMOMERATHSOUTGRABEBEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCHBEWARETHEJUBJUBBIRDANDSHUNTHEFRUMIOUSBANDERSNATCHHETOOKHISVORPALSWORDINHANDLONGTIMETHEMANXOMEFOEHESOUGHTSORESTEDHEBYTHETUMTUMTREEANDSTOODAWHILEINTHOUGHTANDASINUFFISHTHOUGHTHESTOODTHEJABBERWOCKWITHEYESOFFLAMECAMEWHIFFLINGTHROUGHTHETULGEYWOODANDBURBLEDASITCAMEONETWOONETWOANDTHROUGHANDTHROUGHTHEVORPALBLADEWENTSNICKERSNACKHELEFTITDEADANDWITHITSHEADHEWENTGALUMPHINGBACKANDHASTTHOUSLAINTHEJABBERWOCKCOMETOMYARMSMYBEAMISHBOYOFRABJOUSDAYCALLOOHCALLAYHECHORTLEDINHISJOYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIMBLEINTHEWABEALLMIMSYWERETHEBOROGOVESANDTHEMOMERATHSOUTGRABEITSEEMSVERYPRETTYSHESAIDWHENSHEHADFINISHEDITBUTITSRATHERHARDTOUNDERSTAND Elapsed time: 0.041 s
OCaml
Original version by User:Vanyamil.
Uses the Vigenere decrypt function from the Vigenere task solution (not included in the code below).
(* Task : Vigenere cipher/Cryptanalysis *)
(*
Given some text you suspect has been encrypted
with a Vigenère cipher, extract the key and plaintext.
Uses correlation factors similar to other solutions.
(originally tried Friedman test, didn't produce good result)
Coded in a way that allows non-english (by passing frequencies).
*)
(*** Helpers ***)
(* Implementation of Float.round to avoid v4.08 *)
let round (x : float) : float =
let rem = mod_float x 1. in
if rem >= 0.5
then ceil x
else floor x
(* A function that updates array element at a position *)
let array_update (arr : 'a array) (idx : int) (update : 'a -> 'a) : unit =
let curr = Array.get arr idx in
Array.set arr idx (update curr)
(*** Actual task at hand ***)
(* the n'th element of array is how often the n'th letter was found *)
let observe_coincidences ?(step : int = 1) ?(offset : int = 0) (text : string) : int array =
let arr = Array.make 26 0 in
let a_code = Char.code 'A' in
String.iteri (fun idx c -> if idx mod step = offset then array_update arr (Char.code c - a_code) succ) text;
arr
(* Obtain correlation factor for the observed coincidences *)
let correlation_factor ?(sort : bool = true) (coincidences : int array) (freqs : float list) : float =
let clist = Array.to_list coincidences in
let clist = (if sort then List.sort compare clist else clist) in
List.fold_left2 (fun acc c f -> acc +. (float_of_int c *. f)) 0. clist freqs
(* Translation of the test used in other Rosetta Code solutions *)
let shifted_coincidences_test (freqs : float list) (text : string) : int =
let sorted_freqs = List.sort compare freqs in
let bestCorr = -100. in
let max_keylen = String.length text / 20 in
let rec helper idx (cur_len, cur_corr) (best_len, best_corr) =
if cur_len = max_keylen then (* Finished testing everything *)
best_len
else if idx = cur_len then (* Finished testing this key length *)
let (best_len, best_corr) = if cur_corr > best_corr then (cur_len, cur_corr) else (best_len, best_corr) in
helper 0 (cur_len + 1, ~-.0.5 *. float_of_int (cur_len + 1)) (best_len, best_corr)
else
let coincidences = observe_coincidences ~step:cur_len ~offset:idx text in
let factor = correlation_factor coincidences sorted_freqs in
helper (succ idx) (cur_len, cur_corr +. factor) (best_len, best_corr)
in
helper 0 (2, ~-.1.) (1, ~-.100.)
(* Returns the most likely shift value for this set *)
let break_caesar ?(step : int = 1) ?(offset : int = 0) (text : string) (freqs : float list) : int =
let c_arr = observe_coincidences ~step ~offset text in
let rec helper l curShift (maxShift, maxCorr) =
if curShift = 26
then maxShift
else
let corr = correlation_factor ~sort:false c_arr l in
let l' = List.tl l @ [List.hd l] in
if corr > maxCorr
then helper l' (curShift + 1) (curShift, corr)
else helper l' (curShift + 1) (maxShift, maxCorr)
in
helper freqs 0 (-1, -100.)
let break (keylen : int) (text : string) (freqs : float list) : key =
let rec getCaesars idx acc =
if idx >= keylen then acc else
let shift = break_caesar ~step:keylen ~offset:idx text freqs in
let new_code = if shift = 0 then Char.code 'A' else Char.code 'Z' + 1 - shift in
getCaesars (succ idx) (acc ^ Char.(new_code |> chr |> escaped))
in
getCaesars 0 ""
let cryptanalyze (freqs : float list) (text : string) : key * string =
let text = ascii_upper_letters_only text in
let keylen = shifted_coincidences_test freqs text in
let key = break keylen text freqs in
let pt = decrypt key text in
(key, pt)
(*** Output ***)
let _ =
let long_text = "\
MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH \
VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD \
ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS \
FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG \
ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ \
ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS \
JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT \
LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST \
MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH \
QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV \
RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW \
TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO \
SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR \
ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX \
BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB \
BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA \
FWAML ZZRXJ EKAHV FASMU LVVUT TGK"
in
let english_freqs = [
0.08167; 0.01492; 0.02782; 0.04253; 0.12702; 0.02228; 0.02015;
0.06094; 0.06966; 0.00153; 0.00772; 0.04025; 0.02406; 0.06749;
0.07507; 0.01929; 0.00095; 0.05987; 0.06327; 0.09056; 0.02758;
0.00978; 0.02360; 0.00150; 0.01974; 0.00074
]
in
let (key, pt) = cryptanalyze english_freqs long_text in
Printf.printf "Key: %s\n\nText: %s" key pt
;;
- Output:
Key: THECHESHIRECAT Text: THISWASTHEPOEMTHATALICEREADJABBERWOC...
Perl
use strict;
use warnings;
use feature 'say';
# from Wikipedia
my %English_letter_freq = (
E => 12.70, L => 4.03, Y => 1.97, P => 1.93, T => 9.06, A => 8.17, O => 7.51, I => 6.97, N => 6.75,
S => 6.33, H => 6.09, R => 5.99, D => 4.25, C => 2.78, U => 2.76, M => 2.41, W => 2.36, F => 2.23,
G => 2.02, B => 1.29, V => 0.98, K => 0.77, J => 0.15, X => 0.15, Q => 0.10, Z => 0.07
);
my @alphabet = sort keys %English_letter_freq;
my $max_key_lengths = 5; # number of keylengths to try
sub myguess {
my ($text) = (@_);
my ($seqtext, @spacing, @factors, @sortedfactors, $pos, %freq, %Keys);
# Kasiski examination
$seqtext = $text;
while ($seqtext =~ /(...).*\1/) {
$seqtext = substr($seqtext, 1+index($seqtext, $1));
push @spacing, 1 + index($seqtext, $1);
}
for my $j (@spacing) {
push @factors, grep { $j % $_ == 0 } 2..$j;
}
$freq{$_}++ for @factors;
@sortedfactors = grep { $_ >= 4 } sort { $freq{$b} <=> $freq{$a} } keys %freq; # discard very short keys
for my $keylen ( @sortedfactors[0..$max_key_lengths-1] ) {
my $keyguess = '';
for (my $i = 0; $i < $keylen; $i++) {
my($mykey, %chi_values, $bestguess);
for (my $j = 0; $j < length($text); $j += $keylen) {
$mykey .= substr($text, ($j+$i) % length($text), 1);
}
for my $subkey (@alphabet) {
my $decrypted = mycrypt($mykey, $subkey);
my $length = length($decrypted);
for my $char (@alphabet) {
my $expected = $English_letter_freq{$char} * $length / 100;
my $observed;
++$observed while $decrypted =~ /$char/g;
$chi_values{$subkey} += ($observed - $expected)**2 / $expected if $observed;
}
}
$Keys{$keylen}{score} = $chi_values{'A'};
for my $sk (sort keys %chi_values) {
if ($chi_values{$sk} <= $Keys{$keylen}{score}) {
$bestguess = $sk;
$Keys{$keylen}{score} = $chi_values{$sk};
}
}
$keyguess .= $bestguess;
}
$Keys{$keylen}{key} = $keyguess;
}
map { $Keys{$_}{key} } sort { $Keys{$a}{score} <=> $Keys{$b}{score}} keys %Keys;
}
sub mycrypt {
my ($text, $key) = @_;
my ($new_text, %values_numbers);
my $keylen = length($key);
@values_numbers{@alphabet} = 0..25;
my %values_letters = reverse %values_numbers;
for (my $i = 0; $i < length($text); $i++) {
my $val = -1 * $values_numbers{substr( $key, $i%$keylen, 1)} # negative shift for decode
+ $values_numbers{substr($text, $i, 1)};
$new_text .= $values_letters{ $val % 26 };
}
return $new_text;
}
my $cipher_text = <<~'EOD';
MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH
VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD
ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS
FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG
ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ
ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS
JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT
LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST
MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH
QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV
RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW
TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO
SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR
ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX
BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB
BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA
FWAML ZZRXJ EKAHV FASMU LVVUT TGK
EOD
my $text = uc($cipher_text) =~ s/[^@{[join '', @alphabet]}]//gr;
for my $key ( myguess($text) ) {
say "Key $key\n" .
"Key length " . length($key) . "\n" .
"Plaintext " . substr(mycrypt($text, $key), 0, 80) . "...\n";
}
- Output:
Key THECHESHIRECAT Key length 14 Plaintext THISWASTHEPOEMTHATALICEREADJABBERWOCKYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIMB... Key THECHESCIRECATTHECHESHIRECAT Key length 28 Plaintext THISWASYHEPOEMTHATALICEREADJABBERWOHKYTWASBRILLIGANDTHESLITHYTOAESDIDGYREANDGIMB... Key TJGGAHET Key length 8 Plaintext TFGODXGHWMNKEYIVLMBJACIPPTXWTBBNFRADSITFHCOSMGOTFYPOXCASLGRDFQCJTABEDSNFPTOBYIQZ... Key THECSAS Key length 7 Plaintext THISLESHIRRYENTHATPPIQFEGKDKABBEGAOQLLVGATBRILAMGOOQVRETLITHNXOJFFFSDHYREACHGWNO... Key THEC Key length 4 Plaintext THISKXGYWOPOLYIMLODNHCIGPVZAABBEFTCHZITWHEQWTGOKFARSECAJLITHMQCATCDIKSNWPVQFFIQQ...
Phix
-- -- demo\rosetta\Cryptanalysis.exw -- with javascript_semantics atom t0 = time() constant ciphertext = substitute_all(""" MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA FWAML ZZRXJ EKAHV FASMU LVVUT TGK""",{" ","\n"},{"",""}) constant letters = new_dict( {{'E',12.702}, {'T',9.056}, {'A',8.167}, {'O',7.507}, {'I',6.966}, {'N',6.749}, {'S',6.327}, {'H',6.094}, {'R',5.987}, {'D',4.253}, {'L',4.025}, {'C',2.782}, {'U',2.758}, {'M',2.406}, {'W',2.361}, {'F',2.228}, {'G',2.015}, {'Y',1.974}, {'P',1.929}, {'B',1.492}, {'V',0.978}, {'K',0.772}, {'J',0.153}, {'X',0.150}, {'Q',0.095}, {'Z',0.074}}) constant digraphs = new_dict( {{"TH",15.2}, {"HE",12.8}, {"IN",9.4}, {"ER",9.4}, {"AN",8.2}, {"RE",6.8}, {"ND",6.3}, {"AT",5.9}, {"ON",5.7}, {"NT",5.6}, {"HA",5.6}, {"ES",5.6}, {"ST",5.5}, {"EN",5.5}, {"ED",5.3}, {"TO",5.2}, {"IT",5.0}, {"OU",5.0}, {"EA",4.7}, {"HI",4.6}, {"IS",4.6}, {"OR",4.3}, {"TI",3.4}, {"AS",3.3}, {"TE",2.7}, {"ET",1.9}, {"NG",1.8}, {"OF",1.6}, {"AL",0.9}, {"DE",0.9}, {"SE",0.8}, {"LE",0.8}, {"SA",0.6}, {"SI",0.5}, {"AR",0.4}, {"VE",0.4}, {"RA",0.4}, {"LD",0.2}, {"UR",0.2}}) constant trigraphs = new_dict( {{"THE",18.1}, {"AND",7.3}, {"ING",7.2}, {"ION",4.2}, {"ENT",4.2}, {"HER",3.6}, {"FOR",3.4}, {"THA",3.3}, {"NTH",3.3}, {"INT",3.2}, {"TIO",3.1}, {"ERE",3.1}, {"TER",3.0}, {"EST",2.8}, {"ERS",2.8}, {"HAT",2.6}, {"ATI",2.6}, {"ATE",2.5}, {"ALL",2.5}, {"VER",2.4}, {"HIS",2.4}, {"HES",2.4}, {"ETH",2.4}, {"OFT",2.2}, {"STH",2.1}, {"RES",2.1}, {"OTH",2.1}, {"ITH",2.1}, {"FTH",2.1}, {"ONT",2.0}}) function decrypt(string enc, string key) integer keylen = length(key), k = 1 string msg = repeat(' ', length(enc)) for i=1 to length(enc) do msg[i] = mod(enc[i]-key[k]+26,26)+'A' k = mod(k,keylen)+1 end for return msg end function function cryptanalyze(string enc, integer maxkeylen=20) integer enclen = length(enc) string maxkey = "", maxdec = "", k1 = " " atom maxscore = 0.0 for keylen=1 to maxkeylen do string key = repeat(' ',keylen) sequence idx = {} for i=1 to enclen do if mod(i,keylen)=0 then idx &= i-keylen+1 end if end for for i=1 to keylen do atom maxsubscore = 0.0 for j='A' to 'Z' do atom subscore = 0.0 k1[1] = j string encidx = "" for ii=1 to length(idx) do encidx &= enc[idx[ii]] end for string dec = decrypt(encidx,k1) for di=1 to length(dec) do subscore += getd(dec[di],letters) end for if subscore > maxsubscore then maxsubscore = subscore key[i] = j end if end for idx = sq_add(idx,1) end for string dec = decrypt(enc, key) atom score = 0.0 for i=1 to length(dec) do score += getd(dec[i],letters) end for for i=1 to enclen - 2 do string digraph = dec[i..i+1] string trigraph = dec[i..i + 2] score += 2 * getd(digraph,digraphs) score += 3 * getd(trigraph,trigraphs) end for if score > maxscore then maxscore = score maxkey = key maxdec = dec end if end for return {maxkey,maxdec} end function function fold(string s, integer w) for i=w to length(s) by w do s[i..i-1] = "\n" end for return s end function string {key, dec} = cryptanalyze(ciphertext) printf(1,"key: %s\n\n%s\n\n", {key, fold(dec,80)}) printf(1,"elapsed time: %3.2f seconds",{time()-t0}) {} = wait_key()
- Output:
key: THECHESHIRECAT THISWASTHEPOEMTHATALICEREADJABBERWOCKYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIM BLEINTHEWABEALLMIMSYWERETHEBOROGOVESANDTHEMOMERATHSOUTGRABEBEWARETHEJABBERWOCKM YSONTHEJAWSTHATBITETHECLAWSTHATCATCHBEWARETHEJUBJUBBIRDANDSHUNTHEFRUMIOUSBANDER SNATCHHETOOKHISVORPALSWORDINHANDLONGTIMETHEMANXOMEFOEHESOUGHTSORESTEDHEBYTHETUM TUMTREEANDSTOODAWHILEINTHOUGHTANDASINUFFISHTHOUGHTHESTOODTHEJABBERWOCKWITHEYESO FFLAMECAMEWHIFFLINGTHROUGHTHETULGEYWOODANDBURBLEDASITCAMEONETWOONETWOANDTHROUGH ANDTHROUGHTHEVORPALBLADEWENTSNICKERSNACKHELEFTITDEADANDWITHITSHEADHEWENTGALUMPH INGBACKANDHASTTHOUSLAINTHEJABBERWOCKCOMETOMYARMSMYBEAMISHBOYOFRABJOUSDAYCALLOOH CALLAYHECHORTLEDINHISJOYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIMBLEINTHEWABEAL LMIMSYWERETHEBOROGOVESANDTHEMOMERATHSOUTGRABEITSEEMSVERYPRETTYSHESAIDWHENSHEHAD FINISHEDITBUTITSRATHERHARDTOUNDERSTAND elapsed time: 0.42 seconds
Python
from string import uppercase
from operator import itemgetter
def vigenere_decrypt(target_freqs, input):
nchars = len(uppercase)
ordA = ord('A')
sorted_targets = sorted(target_freqs)
def frequency(input):
result = [[c, 0.0] for c in uppercase]
for c in input:
result[c - ordA][1] += 1
return result
def correlation(input):
result = 0.0
freq = frequency(input)
freq.sort(key=itemgetter(1))
for i, f in enumerate(freq):
result += f[1] * sorted_targets[i]
return result
cleaned = [ord(c) for c in input.upper() if c.isupper()]
best_len = 0
best_corr = -100.0
# Assume that if there are less than 20 characters
# per column, the key's too long to guess
for i in xrange(2, len(cleaned) // 20):
pieces = [[] for _ in xrange(i)]
for j, c in enumerate(cleaned):
pieces[j % i].append(c)
# The correlation seems to increase for smaller
# pieces/longer keys, so weigh against them a little
corr = -0.5 * i + sum(correlation(p) for p in pieces)
if corr > best_corr:
best_len = i
best_corr = corr
if best_len == 0:
return ("Text is too short to analyze", "")
pieces = [[] for _ in xrange(best_len)]
for i, c in enumerate(cleaned):
pieces[i % best_len].append(c)
freqs = [frequency(p) for p in pieces]
key = ""
for fr in freqs:
fr.sort(key=itemgetter(1), reverse=True)
m = 0
max_corr = 0.0
for j in xrange(nchars):
corr = 0.0
c = ordA + j
for frc in fr:
d = (ord(frc[0]) - c + nchars) % nchars
corr += frc[1] * target_freqs[d]
if corr > max_corr:
m = j
max_corr = corr
key += chr(m + ordA)
r = (chr((c - ord(key[i % best_len]) + nchars) % nchars + ordA)
for i, c in enumerate(cleaned))
return (key, "".join(r))
def main():
encoded = """
MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH
VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD
ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS
FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG
ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ
ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS
JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT
LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST
MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH
QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV
RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW
TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO
SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR
ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX
BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB
BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA
FWAML ZZRXJ EKAHV FASMU LVVUT TGK"""
english_frequences = [
0.08167, 0.01492, 0.02782, 0.04253, 0.12702, 0.02228, 0.02015,
0.06094, 0.06966, 0.00153, 0.00772, 0.04025, 0.02406, 0.06749,
0.07507, 0.01929, 0.00095, 0.05987, 0.06327, 0.09056, 0.02758,
0.00978, 0.02360, 0.00150, 0.01974, 0.00074]
(key, decoded) = vigenere_decrypt(english_frequences, encoded)
print "Key:", key
print "\nText:", decoded
main()
Racket
Simple method
This is a simple method that just tries to find a key of any length that minimizes the difference from the expected English character distributions.
#lang at-exp racket
(define max-keylen 30)
(define text
@~a{MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH
VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD
ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS
FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG
ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ
ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS
JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT
LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST
MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH
QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV
RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW
TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO
SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR
ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX
BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB
BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA
FWAML ZZRXJ EKAHV FASMU LVVUT TGK})
(define first-char (char->integer #\A))
(define chars# (- (char->integer #\Z) first-char -1))
(define freqs ; english letter frequencies from wikipedia
((compose1 list->vector (curry map (curryr / 100000.0)))
'(8167 1492 2782 4253 12702 2228 2015 6094 6966 153 772 4025 2406
6749 7507 1929 95 5987 6327 9056 2758 978 2360 150 1974 74)))
(define text* (for/vector ([c (regexp-replace* #px"\\s+" text "")])
(- (char->integer c) first-char)))
(define N (vector-length text*))
(define (col-guesses len)
(for/list ([ofs len])
(define text (for/list ([i (in-range ofs N len)]) (vector-ref text* i)))
(define cN (length text))
(define cfreqs (make-vector chars# 0))
(for ([c (in-list text)])
(vector-set! cfreqs c (add1 (vector-ref cfreqs c))))
(for ([i chars#]) (vector-set! cfreqs i (/ (vector-ref cfreqs i) cN)))
(argmin car
(for/list ([d chars#])
(cons (for/sum ([i chars#])
(expt (- (vector-ref freqs i)
(vector-ref cfreqs (modulo (+ i d) chars#)))
2))
d)))))
(define best-key
(cdr (argmin car
(for/list ([len (range 1 (add1 max-keylen))])
(define guesses (col-guesses len))
(cons (/ (apply + (map car guesses)) len) (map cdr guesses))))))
(printf "Best key found: ")
(for ([c best-key]) (display (integer->char (+ c first-char))))
(newline)
(printf "Decoded text:\n")
(define decode-num
(let ([cur '()])
(λ(n) (when (null? cur) (set! cur best-key))
(begin0 (modulo (- n (car cur)) chars#) (set! cur (cdr cur))))))
(for ([c text])
(define n (- (char->integer c) first-char))
(if (not (< -1 n chars#)) (display c)
(display (integer->char (+ first-char (decode-num n))))))
(newline)
Output:
Best key found: THECHESHIRECAT Decoded text: THISW ASTHE POEMT HATAL ICERE ADJAB BERWO CKYTW ASBRI LLIGA ...
An attempted more complete implementation
This is an attempt at following the Wikipedia description. However, it performs just as well as the simple version. Most likely because I know almost nothing about cryptography...
#lang at-exp racket
(define max-keylen 30)
(define text
@~a{MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH
VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD
ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS
FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG
ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ
ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS
JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT
LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST
MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH
QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV
RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW
TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO
SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR
ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX
BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB
BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA
FWAML ZZRXJ EKAHV FASMU LVVUT TGK})
(define first-char (char->integer #\A))
(define chars# (- (char->integer #\Z) first-char -1))
(define freqs ; english letter frequencies from wikipedia
((compose1 list->vector (curry map (curryr / 100000.0)))
'(8167 1492 2782 4253 12702 2228 2015 6094 6966 153 772 4025 2406
6749 7507 1929 95 5987 6327 9056 2758 978 2360 150 1974 74)))
(define (n*n-1 n) (* n (sub1 n)))
(define text* (for/vector ([c (regexp-replace* #px"\\s+" text "")])
(- (char->integer c) first-char)))
(define N (vector-length text*))
(define (get-col-length+freqs width offset)
(define text (for/list ([i (in-range offset N width)]) (vector-ref text* i)))
(define cN (length text))
(define freqs (make-vector chars# 0))
(for ([c (in-list text)]) (vector-set! freqs c (add1 (vector-ref freqs c))))
(values cN freqs))
(define expected-IC (* chars# (for*/sum ([x freqs]) (* x x))))
;; maps key lengths to average index of coincidence
(define keylen->ICs
(for/vector ([len (in-range 1 (add1 (* max-keylen 2)))])
(for/sum ([ofs len])
(define-values [cN cfreqs] (get-col-length+freqs len ofs))
(/ (for/sum ([i chars#]) (n*n-1 (vector-ref cfreqs i)))
(/ (n*n-1 cN) chars#) len 1.0))))
;; given a key length find the key that minimizes errors from alphabet freqs,
;; return (cons average-error key)
(define (guess-key len)
(define guesses
(for/list ([ofs len])
(define-values [cN cfreqs] (get-col-length+freqs len ofs))
(for ([i chars#]) (vector-set! cfreqs i (/ (vector-ref cfreqs i) cN)))
(argmin car
(for/list ([d chars#])
(cons (for/sum ([i chars#])
(expt (- (vector-ref freqs i)
(vector-ref cfreqs (modulo (+ i d) chars#)))
2))
d)))))
(cons (/ (apply + (map car guesses)) len) (map cdr guesses)))
;; look for a key length that minimizes error from expected-IC, with some
;; stupid consideration of multiples of the length (which should also have low
;; errors), for each one guess a key, then find the one that minimizes both (in
;; a way that looks like it works, but undoubtedly is wrong in all kinds of
;; ways) and return the winner key
(define best-key
((compose1 cdr (curry argmin car))
(for/list ([i (* max-keylen 2)])
;; get the error from the expected-IC for the length and its multiples,
;; with decreasing weights for the multiples
(define with-multiples
(for/list ([j (in-range i (* max-keylen 2) (add1 i))] [div N])
(cons (/ (abs (- (vector-ref keylen->ICs j) expected-IC)) expected-IC)
(/ (add1 div)))))
(define total (/ (for/sum ([x with-multiples]) (* (car x) (cdr x)))
(for/sum ([x with-multiples]) (cdr x))))
(define guess (guess-key (add1 i)))
(define guess*total (* total (car guess) (car guess)))
;; (printf "~a~a: ~a ~s\n" (if (< i 9) " " "") (add1 i)
;; (list total (car guess) guess*total) (cdr guess))
(cons guess*total (cdr guess)))))
(printf "Best key found: ")
(for ([c best-key]) (display (integer->char (+ c first-char))))
(newline)
(printf "Decoded text:\n")
(define decode-num
(let ([cur '()])
(λ(n) (when (null? cur) (set! cur best-key))
(begin0 (modulo (- n (car cur)) chars#) (set! cur (cdr cur))))))
(for ([c text])
(define n (- (char->integer c) first-char))
(if (not (< -1 n chars#)) (display c)
(display (integer->char (+ first-char (decode-num n))))))
(newline)
Raku
(formerly Perl 6)
# from Wikipedia
constant %English-letter-freq = (
E => 12.70, L => 4.03, Y => 1.97, P => 1.93, T => 9.06, A => 8.17, O => 7.51, I => 6.97, N => 6.75,
S => 6.33, H => 6.09, R => 5.99, D => 4.25, C => 2.78, U => 2.76, M => 2.41, W => 2.36, F => 2.23,
G => 2.02, B => 1.29, V => 0.98, K => 0.77, J => 0.15, X => 0.15, Q => 0.10, Z => 0.07
);
constant @alphabet = %English-letter-freq.keys.sort;
constant max_key_lengths = 5; # number of keylengths to try
sub myguess ($text) {
my ($seqtext, @spacing, @factors, $pos, %freq, %Keys);
# Kasiski examination
$seqtext = $text;
while ($seqtext ~~ /$<sequence>=[...].*$<sequence>/) {
$seqtext = substr($seqtext, 1+index($seqtext, $<sequence>));
push @spacing, 1 + index($seqtext, $<sequence>);
}
for @spacing -> $j {
%freq{$_}++ for grep { $j %% $_ }, 2..$j;
}
# discard very short keys, and test only the most likely remaining key lengths
(%freq.keys.grep(* > 3).sort({%freq{$_}}).tail(max_key_lengths)).race(:1batch).map: -> $keylen {
my $key-guess = '';
loop (my $i = 0; $i < $keylen; $i++) {
my ($mykey, %chi-square, $best-guess);
loop (my $j = 0; $j < $text.chars; $j += $keylen) {
$mykey ~= substr($text, ($j+$i) % $text.chars, 1);
}
for @alphabet -> $subkey {
my $decrypted = mycrypt($mykey, $subkey);
my $length = $decrypted.chars;
for @alphabet -> $char {
my $expected = %English-letter-freq{$char} * $length / 100;
my $observed = $decrypted.comb.grep(* eq $char).elems;
%chi-square{$subkey} += ($observed - $expected)² / $expected if $observed;
}
}
%Keys{$keylen}{'score'} = %chi-square{@alphabet[0]};
for %chi-square.keys.sort -> $sk {
if (%chi-square{$sk} <= %Keys{$keylen}{'score'}) {
$best-guess = $sk;
%Keys{$keylen}{'score'} = %chi-square{$sk};
}
}
$key-guess ~= $best-guess;
}
%Keys{$keylen}{'key'} = $key-guess;
}
%Keys.keys.sort({ %Keys{$_}{'score'} }).map:{ %Keys{$_}{'key'} };
}
sub mycrypt ($text, $key) {
constant %values-numbers = @alphabet Z=> ^@alphabet;
constant %values-letters = %values-numbers.invert;
my ($new-text);
my $keylen = $key.chars;
loop (my $i = 0; $i < $text.chars; $i++) {
my $val = -1 * %values-numbers{substr( $key, $i%$keylen, 1)} # negative shift for decode
+ %values-numbers{substr($text, $i, 1)};
$new-text ~= %values-letters{ $val % @alphabet };
}
return $new-text;
}
my $cipher-text = .uc.trans(@alphabet => '', :c) given q:to/EOD/;
MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH
VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD
ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS
FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG
ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ
ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS
JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT
LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST
MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH
QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV
RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW
TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO
SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR
ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX
BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB
BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA
FWAML ZZRXJ EKAHV FASMU LVVUT TGK
EOD
for myguess($cipher-text) -> $key {
say "Key $key\n" ~
"Key length {$key.chars}\n" ~
"Plaintext {substr(mycrypt($cipher-text, $key), 0, 80)}...\n";
}
- Output:
Key THECHESHIRECAT Key length 14 Plaintext THISWASTHEPOEMTHATALICEREADJABBERWOCKYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIMB... Key THECHESCIRECATTHECHESHIRECAT Key length 28 Plaintext THISWASYHEPOEMTHATALICEREADJABBERWOHKYTWASBRILLIGANDTHESLITHYTOAESDIDGYREANDGIMB... Key TJGGAHET Key length 8 Plaintext TFGODXGHWMNKEYIVLMBJACIPPTXWTBBNFRADSITFHCOSMGOTFYPOXCASLGRDFQCJTABEDSNFPTOBYIQZ... Key THECSAS Key length 7 Plaintext THISLESHIRRYENTHATPPIQFEGKDKABBEGAOQLLVGATBRILAMGOOQVRETLITHNXOJFFFSDHYREACHGWNO... Key THEC Key length 4 Plaintext THISKXGYWOPOLYIMLODNHCIGPVZAABBEFTCHZITWHEQWTGOKFARSECAJLITHMQCATCDIKSNWPVQFFIQQ...
Rust
Note that the character to/from byte (u8) conversions work here only because the key and cryptogram are composed of ASCII characters only. Indeed, Rust's char type is a Unicode scalar value, how they are represented is well summarized in the Rust book's subchapter on strings.
use std::iter::FromIterator;
const CRYPTOGRAM: &str = "MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH
VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD
ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS
FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG
ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ
ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS
JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT
LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST
MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH
QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV
RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW
TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO
SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR
ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX
BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB
BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA
FWAML ZZRXJ EKAHV FASMU LVVUT TGK";
const FREQUENCIES: [f32; 26] = [
0.08167, 0.01492, 0.02202, 0.04253, 0.12702, 0.02228, 0.02015, 0.06094, 0.06966, 0.00153,
0.01292, 0.04025, 0.02406, 0.06749, 0.07507, 0.01929, 0.00095, 0.05987, 0.06327, 0.09356,
0.02758, 0.00978, 0.02560, 0.00150, 0.01994, 0.00077,
];
fn best_match(a: &[f32]) -> u8 {
let sum: f32 = a.iter().sum();
let mut best_fit = std::f32::MAX;
let mut best_rotate = 0;
for rotate in 0..=25 {
let mut fit = 0.;
for i in 0..=25 {
let char_freq = FREQUENCIES[i];
let idx = (i + rotate as usize) % 26 as usize;
let d = a[idx] / sum - char_freq;
fit += d * d / char_freq;
}
if fit < best_fit {
best_fit = fit;
best_rotate = rotate;
}
}
best_rotate
}
fn freq_every_nth(msg: &[u8], key: &mut [char]) -> f32 {
let len = msg.len();
let interval = key.len();
let mut accu = [0.; 26];
for j in 0..interval {
let mut out = [0.; 26];
for i in (j..len).step_by(interval) {
let idx = msg[i] as usize;
out[idx] += 1.;
}
let rot = best_match(&out);
key[j] = char::from(rot + b'A');
for i in 0..=25 {
let idx: usize = (i + rot as usize) % 26;
accu[i] += out[idx];
}
}
let sum: f32 = accu.iter().sum();
let mut ret = 0.;
for i in 0..=25 {
let char_freq = FREQUENCIES[i];
let d = accu[i] / sum - char_freq;
ret += d * d / char_freq;
}
ret
}
fn decrypt(text: &str, key: &str) -> String {
let key_chars_cycle = key.as_bytes().iter().map(|b| *b as i32).cycle();
let is_ascii_uppercase = |c: &u8| (b'A'..=b'Z').contains(c);
text.as_bytes()
.iter()
.filter(|c| is_ascii_uppercase(c))
.map(|b| *b as i32)
.zip(key_chars_cycle)
.fold(String::new(), |mut acc, (c, key_char)| {
let ci: u8 = ((c - key_char + 26) % 26) as u8;
acc.push(char::from(b'A' + ci));
acc
})
}
fn main() {
let enc = CRYPTOGRAM
.split_ascii_whitespace()
.collect::<Vec<_>>()
.join("");
let cryptogram: Vec<u8> = enc.as_bytes().iter().map(|b| u8::from(b - b'A')).collect();
let mut best_fit = std::f32::MAX;
let mut best_key = String::new();
for j in 1..=26 {
let mut key = vec!['\0'; j];
let fit = freq_every_nth(&cryptogram, &mut key);
let s_key = String::from_iter(key); // 'from_iter' is imported from std::iter::FromIterator;
if fit < best_fit {
best_fit = fit;
best_key = s_key;
}
}
println!("best key: {}", &best_key);
println!("\nDecrypted text:\n{}", decrypt(&enc, &best_key));
}
- Output:
best key: THECHESHIRECAT Decrypted text: THISWASTHEPOEMTHATALICEREADJABBERWOCKYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIMBLEINTHEWABEALLMIMSYWERETHEBOROGOVESANDTHEMOMERATHSOUTGRABEBEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCHBEWARETHEJUBJUBBIRDANDSHUNTHEFRUMIOUSBANDERSNATCHHETOOKHISVORPALSWORDINHANDLONGTIMETHEMANXOMEFOEHESOUGHTSORESTEDHEBYTHETUMTUMTREEANDSTOODAWHILEINTHOUGHTANDASINUFFISHTHOUGHTHESTOODTHEJABBERWOCKWITHEYESOFFLAMECAMEWHIFFLINGTHROUGHTHETULGEYWOODANDBURBLEDASITCAMEONETWOONETWOANDTHROUGHANDTHROUGHTHEVORPALBLADEWENTSNICKERSNACKHELEFTITDEADANDWITHITSHEADHEWENTGALUMPHINGBACKANDHASTTHOUSLAINTHEJABBERWOCKCOMETOMYARMSMYBEAMISHBOYOFRABJOUSDAYCALLOOHCALLAYHECHORTLEDINHISJOYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIMBLEINTHEWABEALLMIMSYWERETHEBOROGOVESANDTHEMOMERATHSOUTGRABEITSEEMSVERYPRETTYSHESAIDWHENSHEHADFINISHEDITBUTITSRATHERHARDTOUNDERSTAND
Tcl
package require Tcl 8.6
oo::class create VigenereAnalyzer {
variable letterFrequencies sortedTargets
constructor {{frequencies {
0.08167 0.01492 0.02782 0.04253 0.12702 0.02228 0.02015
0.06094 0.06966 0.00153 0.00772 0.04025 0.02406 0.06749
0.07507 0.01929 0.00095 0.05987 0.06327 0.09056 0.02758
0.00978 0.02360 0.00150 0.01974 0.00074
}}} {
set letterFrequencies $frequencies
set sortedTargets [lsort -real $frequencies]
if {[llength $frequencies] != 26} {
error "wrong length of frequency table"
}
}
### Utility methods
# Find the value of $idxvar in the range [$from..$to) that maximizes the value
# in $scorevar (which is computed by evaluating $body)
method Best {idxvar from to scorevar body} {
upvar 1 $idxvar i $scorevar s
set bestI $from
for {set i $from} {$i < $to} {incr i} {
uplevel 1 $body
if {![info exist bestS] || $bestS < $s} {
set bestI $i
set bestS $s
}
}
return $bestI
}
# Simple list map
method Map {var list body} {
upvar 1 $var v
set result {}
foreach v $list {lappend result [uplevel 1 $body]}
return $result
}
# Simple partition of $list into $groups groups; thus, the partition of
# {a b c d e f} into 3 produces {a d} {b e} {c f}
method Partition {list groups} {
set i 0
foreach val $list {
dict lappend result $i $val
if {[incr i] >= $groups} {
set i 0
}
}
return [dict values $result]
}
### Helper methods
# Get the actual counts of different types of characters in the given list
method Frequency cleaned {
for {set i 0} {$i < 26} {incr i} {
dict set tbl $i 0
}
foreach ch $cleaned {
dict incr tbl [expr {[scan $ch %c] - 65}]
}
return $tbl
}
# Get the correlation factor of the characters in a given list with the
# class-specified language frequency corpus
method Correlation cleaned {
set result 0.0
set freq [lsort -integer [dict values [my Frequency $cleaned]]]
foreach f $freq s $sortedTargets {
set result [expr {$result + $f * $s}]
}
return $result
}
# Compute an estimate for the key length
method GetKeyLength {cleaned {required 20}} {
# Assume that we need at least 20 characters per column to guess
set bestLength [my Best i 2 [expr {[llength $cleaned] / $required}] corr {
set corr [expr {-0.5 * $i}]
foreach chars [my Partition $cleaned $i] {
set corr [expr {$corr + [my Correlation $chars]}]
}
}]
if {$bestLength == 0} {
error "text is too short to analyze"
}
return $bestLength
}
# Compute the key from the given frequency tables and the class-specified
# language frequency corpus
method GetKeyFromFreqs freqs {
foreach f $freqs {
set m [my Best i 0 26 corr {
set corr 0.0
foreach {ch count} $f {
set d [expr {($ch - $i) % 26}]
set corr [expr {$corr + $count*[lindex $letterFrequencies $d]}]
}
}]
append key [format %c [expr {65 + $m}]]
}
return $key
}
##### The main analyzer method #####
method analyze input {
# Turn the input into a clean letter sequence
set cleaned [regexp -all -inline {[A-Z]} [string toupper $input]]
# Get the (estimated) key length
set bestLength [my GetKeyLength $cleaned]
# Get the frequency mapping for the partitioned input text
set freqs [my Map p [my Partition $cleaned $bestLength] {my Frequency $p}]
# Get the key itself
return [my GetKeyFromFreqs $freqs]
}
}
Demonstration (that assumes that the Tcl solution to Vigenère cipher task is present):
set encoded "
MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH
VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD
ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS
FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG
ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ
ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS
JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT
LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST
MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH
QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV
RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW
TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO
SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR
ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX
BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB
BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA
FWAML ZZRXJ EKAHV FASMU LVVUT TGK
"
VigenereAnalyzer create englishVigenereAnalyzer
set key [englishVigenereAnalyzer analyze $encoded]
Vigenere create decoder $key
set decoded [decoder decrypt $encoded]
puts "Key: $key"
puts "Text: $decoded"
Vedit macro language
This implementation is fully autonomous as long as the text is long enough and there are not too many non-English words in the original text.
The text to be analysed must be in current edit buffer. A new buffer is opened to display the results.
To automatically find the best key, a dictionary is used to find English words within the decrypted text. I have used unixdict.txt, but if you do not have it available, you can use the Scribe English dictionary that comes with Vedit. However, that is unnecessarily big. A smaller dictionary is faster and may actually give better results. It might be good idea to use dictionary that only contains the most common English words.
This implementation finds the best and 2nd best Caesar key for each key position. It then checks key combinations where max one char is taken from 2nd best Caesar key. If this does not solve some encrypted text, you could increase the number of key combinations to be checked.
// (1) Copy text into tmp buffer and remove non-alpha chars.
Chdir(PATH_ONLY)
BOF
Reg_Copy(10, ALL) // copy text to new buffer
Buf_Switch(Buf_Free)
Reg_Ins(10)
BOF
Replace ("|!|A", "", BEGIN+ALL+NOERR) // remove non-alpha chars
Reg_Copy_Block(10,0,EOB_pos) // @10 = text to be analysed
#20 = Buf_Num // buffer for text being analyzed
#21 = Buf_Free // buffer for English frequency list (A-Z)
Buf_Switch(#21)
Ins_Text("8167 1492 2782 4253 12702 2228 2015 6094 6966 153 772 4025 2406 6749 7507 1929 95 5987 6327 9056 2758 978 2360 150 1974 74")
File_Open("unixdict.txt") // or use "|(MACRO_DIR)\scribe\english.vdf"
#23 = Buf_Num // buffer for dictionary
#24 = Buf_Free // buffer for key canditates
Buf_Switch(#24)
for (#1=0; #1<5; #1++) { // Fill table for 5 keys of 50 chars
Ins_Char('.', COUNT, 50)
Ins_Newline
}
#22 = Buf_Free // buffer for results
#25 = Reg_Size(10) // number of letters in the text
#26 = 26 // number of characters in the alphabet
#61 = min(#25/10, 50) // max key length to try
// (2) Check Index of coincidence (or Kp) for each key length
Buf_Switch(#22) // buffer for results
Ins_Text("KeyLen Kp dist ") Ins_Newline
Ins_Text("-----------------") Ins_Newline
#13 = Cur_Pos
#7 = 0 // no Caesar encryption
for (#5=1; #5<=#61; #5++) {
Buf_Switch(#20) // text being analyzed
BOF
#54 = 0; // sum of Kp's
for (#6=0; #6<#5; #6++) { // for each slide
Goto_Pos(#6)
Call("CHARACTER_FREQUENCIES")
Call("INDEX_OF_COINCIDENCE") // #51 = Kp * 10000
#54 += #51
}
#54 /= #5 // average of Kp's
Buf_Switch(#22)
Num_Ins(#5, COUNT, 3) // write key length
IT(": ")
Num_Ins(#54, NOCR) // average Kp
Num_Ins(670-#54) // distance to English Kp
}
Buf_Switch(#22)
Sort_Merge("5,12", #13, Cur_Pos, REVERSE) // sort the results by Kp value
Ins_Newline
// (3) Check the best 4 key lengths to find which one gives the best decrypt result
#38 = 0 // max number of correct characters found
#19 = 1 // best key length
for (#14 = 0; #14<4; #14++) { // try 4 best key lengths
Buf_Switch(#22) // results buffer
Goto_Pos(#13) Line(#14)
#5 = Num_Eval(SUPPRESS) // #5 = key length
Call("FIND_KEYS") // find Caesar key for each key character
#4 = -1 // try best match key chars only
Call("BUILD_KEY")
EOF
Ins_Text("Key length ")
Num_Ins(#5, LEFT)
Reg_Ins(10) // encrypted text
BOL
Call("DECRYPT_LINE")
BOL
Call("FIND_ENGLISH_WORDS") // #37 = number of English chars
EOL Ins_Newline
Ins_Text("Correct chars: ")
Num_Ins(#37)
if (#37 > #38) {
#38 = #37
#19 = #5
}
Update()
}
Ins_Text("Using key length: ") Num_Ins(#19) Ins_Newline
#5 = #19
Call("FIND_KEYS") // find Caesar key for each key character
// (4) Decrypt with different key combinations and try to find English words.
// Try key combinations where max one char is taken from 2nd best Caesar key.
#38 = 0 // max number of chars in English words found
#39 = -1 // best key number found
for (#4 = -1; #4 < #19; #4++)
{
Call("BUILD_KEY")
Buf_Switch(#22) // results
Reg_Ins(10) // encrypted text
BOL
Call("DECRYPT_LINE")
BOL
Update()
Call("FIND_ENGLISH_WORDS") // #37 := number of correct letters in text
if (#37 > #38) {
#38 = #37 // new highest number of correct chars
#39 = #4 // new best key
}
EOL IT(" -- ") // display results
Num_Ins(#4, COUNT, 3) // key number
Ins_Text(": ")
for (#6=0; #6<#19; #6++) { // display key
#9 = 130 + #6
Ins_Char(#@9)
}
Ins_Text(" correct chars =")
Num_Ins(#37)
}
Ins_Text("Best key = ")
Num_Ins(#39, LEFT)
#4 = #39
Ins_Newline
// Display results
//
Buf_Switch(#24) // table for key canditates
BOF
Reg_Copy_Block(14, Cur_Pos, Cur_Pos+#19) // best Caesar key chars
Line(1)
Reg_Copy_Block(15, Cur_Pos, Cur_Pos+#19) // 2nd best Caesar key chars
Call("BUILD_KEY")
Buf_Switch(#22)
Ins_Text("Key 1: ") Reg_Ins(14) Ins_Newline
Ins_Text("Key 2: ") Reg_Ins(15) Ins_Newline
Ins_Text("Key: ")
for (#6=0; #6 < #19; #6++) {
#9 = #6+130
Ins_Char(#@9)
}
Ins_Newline
Ins_Newline
// decrypt the text with selected key
Ins_Text("Decrypted text:") Ins_Newline
Reg_Ins(10)
BOL
Call("DECRYPT_LINE")
BOL Reg_Copy(13,1)
EOL Ins_Newline
// Find English words from the text
Reg_Ins(13)
Call("FIND_ENGLISH_WORDS")
EOL
Ins_Newline
Num_Ins(#37, NOCR) IT(" of ")
Num_Ins(#25, NOCR) IT(" characters are English words. ")
Ins_Newline
Buf_Switch(#20) Buf_Quit(OK)
Buf_Switch(#21) Buf_Quit(OK)
Buf_Switch(#23) Buf_Quit(OK)
Buf_Switch(#24) Buf_Quit(OK)
Statline_Message("Done!")
Return
/////////////////////////////////////////////////////////////////////////////
//
// Caesar decrypt current line and count character frequencies.
// in: #5 = step size, #7 = encryption key, #26 = num of chars in alphabet
// out: #65...#90 = frequencies, #60 = number of chars
:CHARACTER_FREQUENCIES:
Save_Pos
for (#8 = 'A'; #8<='Z'; #8++) {
#@8 = 0 // reset frequency counters
}
#60 = 0 // total number of chars
while (!At_EOL) {
if (Cur_Char >= 'A' && Cur_Char <= 'Z') {
#8 = (Cur_Char-'A'+#26-#7) % #26 + 'A' // decrypted char
#@8++
#60++
}
Char(#5)
}
Restore_Pos
Return
// Calculate Index of Coincidence (Kp).
// in: character frequencies in #65...#90, #60 = num of chars
// out: #51 = IC * 10000
//
:INDEX_OF_COINCIDENCE:
Num_Push(10,15)
#10 = 0
for (#11 = 'A'; #11<='Z'; #11++) {
#10 += (#@11 * (#@11-1)) // Calculate sigma{ni * (ni-1)}
}
#12 = #60 * (#60-1) // #12 = N * (N-1)
#51 = #10 * 10000 / #12 // #51 = Kp * 10000
Num_Pop(10,15)
Return
// Find best and 2nd best Caesar key for each character position of Vigenère key.
// in: #5=step size (key length)
// out: keys in buffer #24
//
:FIND_KEYS:
for (#6 = 0; #6 < #5; #6++) { // for each char position in the key
#30 = -1 // best key char found so far
#31 = -1 // 2nd best key char
#32 = MAXNUM // smallest error found so far
#33 = MAXNUM // 2nd smallest error found so far
for (#7 = 0; #7 < #26; #7++) { // for each possible key value
#35 = 0 // total frequency error compared to English
Buf_Switch(#20) // text being analyzed
Goto_Pos(#6)
Call("CHARACTER_FREQUENCIES")
Buf_Switch(#21) // English frequency table
BOF
for (#8 = 'A'; #8<='Z'; #8++) { // calculate total frequency error
#34 = Num_Eval(SUPPRESS+ADVANCE)
#35 += abs((#@8*100000+50000)/#60-#34)
}
if (#35 < #32) { // found better match?
#33 = #32
#32 = #35
#31 = #30
#30 = #7
} else {
if (#35 < #33) { // 2nd best match?
#33 = #35
#31 = #7
}
}
}
Buf_Switch(#24) // table for key canditates
BOF
Goto_Col(#6+1)
Ins_Char(#30+'A', OVERWRITE) // save the best match
Line(1)
Goto_Col(#6+1)
Ins_Char(#31+'A', OVERWRITE) // save 2nd best match
}
Buf_Switch(#22) // results buffer
Return
// Combine actual key from 1st and 2nd best Caesar key characters
// Use 1st key chars and (possibly) one character from 2nd key.
// #4 = index of the char to be picked from 2nd key, -1 = none.
// #5 = key length
//
:BUILD_KEY:
Buf_Switch(#24) // table for key canditates
BOF
for (#6=0; #6<#5; #6++) { // copy 1st key
#8 = 130 + #6
#@8 = Cur_Char
Char(1)
}
if (#4 >= 0) {
#8 = 130 + #4 // pick one char from 2st key
Line(1)
Goto_Col(#4+1)
#@8 = Cur_Char
}
Buf_Switch(#22) // results buffer
Return
// Decrypt text on current line
// in: #5 = key length, #130...#189 = key
//
:DECRYPT_LINE:
Num_Push(6,9)
#6 = 0
While (!At_EOL) {
#9 = #6+130
#7 = #@9
#8 = (Cur_Char - #7 + #26) % #26 + 'A' // decrypted char
Ins_Char(#8, OVERWRITE)
#6++
if (#6 >= #5) {
#6 = 0
}
}
Num_Pop(6,9)
Return
// Find English words from text on current line
// out: #37 = number of chars matched
//
:FIND_ENGLISH_WORDS:
Buf_Switch(#23) // dictionary
BOF
While (!At_EOF) {
Reg_Copy_Block(12, Cur_Pos, EOL_Pos)
if (Reg_Size(12) > 2) {
Buf_Switch(#22) // buffer for results
BOL
while (Search_Block(@12, Cur_Pos, EOL_Pos, NOERR)) {
Reg_Ins(12, OVERWRITE)
}
Buf_Switch(#23)
}
Line(1, ERRBREAK)
}
Buf_Switch(#22)
BOL
#37 = Search_Block("|V", Cur_Pos, EOL_Pos, ALL+NOERR)
Return
V (Vlang)
import strings
const encoded =
"MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH" +
"VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD" +
"ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS" +
"FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG" +
"ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ" +
"ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS" +
"JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT" +
"LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST" +
"MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH" +
"QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV" +
"RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW" +
"TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO" +
"SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR" +
"ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX" +
"BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB" +
"BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA" +
"FWAML ZZRXJ EKAHV FASMU LVVUT TGK"
const freq = [
0.08167, 0.01492, 0.02782, 0.04253, 0.12702, 0.02228, 0.02015,
0.06094, 0.06966, 0.00153, 0.00772, 0.04025, 0.02406, 0.06749,
0.07507, 0.01929, 0.00095, 0.05987, 0.06327, 0.09056, 0.02758,
0.00978, 0.02360, 0.00150, 0.01974, 0.00074,
]
fn sum(a []f64) f64 {
mut s := 0.0
for f in a {
s += f
}
return s
}
fn best_match(a []f64) int {
s := sum(a)
mut best_fit, mut best_rotate := 1e100, 0
for rotate in 0..26 {
mut fit := 0.0
for i in 0..26 {
d := a[(i+rotate)%26]/s - freq[i]
fit += d * d / freq[i]
}
if fit < best_fit {
best_fit, best_rotate = fit, rotate
}
}
return best_rotate
}
fn freq_every_nth(msg []int, mut key []u8) f64 {
l := msg.len
interval := key.len
mut out := []f64{len: 26}
mut accu := []f64{len: 26}
for j in 0..interval {
for z in 0..26 {
out[z] = 0.0
}
for i := j; i < l; i += interval {
out[msg[i]]++
}
rot := best_match(out)
key[j] = u8(rot + 65)
for i := 0; i < 26; i++ {
accu[i] += out[(i+rot)%26]
}
}
s := sum(accu)
mut ret := 0.0
for i := 0; i < 26; i++ {
d := accu[i]/s - freq[i]
ret += d * d / freq[i]
}
return ret
}
fn decrypt(text string, key string) string {
mut sb := strings.new_builder(128)
mut ki := 0
for c in text {
if c < 'A'[0] || c > 'Z'[0] {
continue
}
ci := (c - key[ki] + 26) % 26
sb.write_rune(ci + 65)
ki = (ki + 1) % key.len
}
return sb.str()
}
fn main() {
enc := encoded.replace(" ", "")
mut txt := []int{len: enc.len}
for i in 0..txt.len {
txt[i] = int(enc[i] - 'A'[0])
}
mut best_fit, mut best_key := 1e100, ""
println(" Fit Length Key")
for j := 1; j <= 26; j++ {
mut key := []u8{len: j}
fit := freq_every_nth(txt, mut key)
s_key := key.bytestr()
print("${fit:.6} ${j:2} $s_key")
if fit < best_fit {
best_fit, best_key = fit, s_key
print(" <--- best so far")
}
println('')
}
println("\nBest key : $best_key")
println("\nDecrypted text:\n${decrypt(enc, best_key)}")
}
- Output:
Note: carriage returns inserted into decrypted text after every 80 characters to make it more readable.
Fit Length Key 2.984348 1 E <--- best so far 2.483684 2 EC <--- best so far 2.642487 3 TEE 1.976651 4 THEC <--- best so far 2.356881 5 EEEPU 2.203129 6 TCECEC 1.051163 7 THECSAS <--- best so far 1.645763 8 TJQGAHET 2.001380 9 VEIZSEGNT 1.824476 10 ECEGAWQTDS 1.623083 11 TNLUSRXPTAJ 1.253527 12 XLECTHQGTHEC 1.399037 13 LJJTDGFNOTENR 0.152370 14 THECHESHIRECAT <--- best so far 1.533951 15 JNTOOEEXFTGQTNH 1.068182 16 TJTSAEETEXHPXHNE 1.034093 17 AZRAXUHEJLREEXIEE 1.443345 18 VNIZQPALEPTSXSEXUC 1.090977 19 FUCAITCSLVTEZDUDEHS 0.979868 20 EQXGAHWTTQECEWUGXHPI 0.789410 21 HVRCSAFTHEBDLSTAERSES 0.881380 22 TVIJTCIGKAQPELECRXPTNC 0.952456 23 KKEQXGPWTCQEELIEHXUWASV 0.715968 24 ELAIXHQTTIEDXJETTNTGAEPC 0.891258 25 OTJUUEGERDNQTUQEAGWUTIEOA 0.852784 26 IGITEGECAGAVUNLJAHASAVTETW Best key : THECHESHIRECAT Decrypted text: THISWASTHEPOEMTHATALICEREADJABBERWOCKYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIMB LEINTHEWABEALLMIMSYWERETHEBOROGOVESANDTHEMOMERATHSOUTGRABEBEWARETHEJABBERWOCKMYS ONTHEJAWSTHATBITETHECLAWSTHATCATCHBEWARETHEJUBJUBBIRDANDSHUNTHEFRUMIOUSBANDERSNA TCHHETOOKHISVORPALSWORDINHANDLONGTIMETHEMANXOMEFOEHESOUGHTSORESTEDHEBYTHETUMTUMT REEANDSTOODAWHILEINTHOUGHTANDASINUFFISHTHOUGHTHESTOODTHEJABBERWOCKWITHEYESOFFLAM ECAMEWHIFFLINGTHROUGHTHETULGEYWOODANDBURBLEDASITCAMEONETWOONETWOANDTHROUGHANDTHR OUGHTHEVORPALBLADEWENTSNICKERSNACKHELEFTITDEADANDWITHITSHEADHEWENTGALUMPHINGBACK ANDHASTTHOUSLAINTHEJABBERWOCKCOMETOMYARMSMYBEAMISHBOYOFRABJOUSDAYCALLOOHCALLAYHE CHORTLEDINHISJOYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIMBLEINTHEWABEALLMIMSYWER ETHEBOROGOVESANDTHEMOMERATHSOUTGRABEITSEEMSVERYPRETTYSHESAIDWHENSHEHADFINISHEDIT BUTITSRATHERHARDTOUNDERSTAND
Wren
import "./math" for Nums
import "./iterate" for Stepped
import "./str" for Char, Str
import "./fmt" for Fmt
var encoded =
"MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH" +
"VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD" +
"ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS" +
"FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG" +
"ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ" +
"ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS" +
"JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT" +
"LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST" +
"MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH" +
"QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV" +
"RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW" +
"TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO" +
"SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR" +
"ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX" +
"BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB" +
"BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA" +
"FWAML ZZRXJ EKAHV FASMU LVVUT TGK"
var freq = [
0.08167, 0.01492, 0.02782, 0.04253, 0.12702, 0.02228, 0.02015,
0.06094, 0.06966, 0.00153, 0.00772, 0.04025, 0.02406, 0.06749,
0.07507, 0.01929, 0.00095, 0.05987, 0.06327, 0.09056, 0.02758,
0.00978, 0.02360, 0.00150, 0.01974, 0.00074
]
var bestMatch = Fn.new { |a|
var sum = Nums.sum(a)
var bestFit = 1e100
var bestRotate = 0
for (rotate in 0..25) {
var fit = 0
for (i in 0..25) {
var d = a[(i + rotate) % 26] / sum - freq[i]
fit = fit + d * d / freq[i]
}
if (fit < bestFit) {
bestFit = fit
bestRotate = rotate
}
}
return bestRotate
}
var freqEveryNth = Fn.new { |msg, key|
var len = msg.count
var interval = key.count
var out = List.filled(26, 0)
var accu = List.filled(26, 0)
for (j in 0...interval) {
for (i in 0..25) out[i] = 0
for (i in Stepped.new(j...len, interval)) out[msg[i]] = out[msg[i]] + 1
var rot = bestMatch.call(out)
key[j] = Char.fromCode(rot + 65)
for (i in 0..25) accu[i] = accu[i] + out[(i + rot) % 26]
}
var sum = Nums.sum(accu)
var ret = 0
for (i in 0..25) {
var d = accu[i] / sum - freq[i]
ret = ret + d * d / freq[i]
}
return ret
}
var decrypt = Fn.new { |text, key|
var sb = ""
var ki = 0
for (c in text) {
if (Char.isAsciiUpper(c)) {
var ci = (c.bytes[0] - key[ki].bytes[0] + 26) % 26
sb = sb + Char.fromCode(ci + 65)
ki = (ki + 1) % key.count
}
}
return sb
}
var enc = encoded.replace(" ", "")
var txt = List.filled(enc.count, 0)
for (i in 0...txt.count) txt[i] = Char.code(enc[i]) - 65
var bestFit = 1e100
var bestKey = ""
var f = "$f $2d $s"
System.print(" Fit Length Key")
for (j in 1..26) {
var key = List.filled(j, "")
var fit = freqEveryNth.call(txt, key)
var sKey = key.join("")
Fmt.write(f, fit, j, sKey)
if (fit < bestFit) {
bestFit = fit
bestKey = sKey
System.write(" <--- best so far")
}
System.print()
}
System.print()
System.print("Best key : %(bestKey)")
System.print("\nDecrypted text:\n%(decrypt.call(enc, bestKey))")
- Output:
Fit Length Key 2.984348 1 E <--- best so far 2.483684 2 EC <--- best so far 2.642487 3 TEE 1.976651 4 THEC <--- best so far 2.356881 5 EEEPU 2.203129 6 TCECEC 1.051163 7 THECSAS <--- best so far 1.645763 8 TJQGAHET 2.001380 9 VEIZSEGNT 1.824476 10 ECEGAWQTDS 1.623083 11 TNLUSRXPTAJ 1.253527 12 XLECTHQGTHEC 1.399037 13 LJJTDGFNOTENR 0.152370 14 THECHESHIRECAT <--- best so far 1.533951 15 JNTOOEEXFTGQTNH 1.068182 16 TJTSAEETEXHPXHNE 1.034093 17 AZRAXUHEJLREEXIEE 1.443345 18 VNIZQPALEPTSXSEXUC 1.090977 19 FUCAITCSLVTEZDUDEHS 0.979868 20 EQXGAHWTTQECEWUGXHPI 0.789410 21 HVRCSAFTHEBDLSTAERSES 0.881380 22 TVIJTCIGKAQPELECRXPTNC 0.952456 23 KKEQXGPWTCQEELIEHXUWASV 0.715968 24 ELAIXHQTTIEDXJETTNTGAEPC 0.891258 25 OTJUUEGERDNQTUQEAGWUTIEOA 0.852784 26 IGITEGECAGAVUNLJAHASAVTETW Best key : THECHESHIRECAT Decrypted text: THISWASTHEPOEMTHATALICEREADJABBERWOCKYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIMBLEINTHEWABEALLMIMSYWERETHEBOROGOVESANDTHEMOMERATHSOUTGRABEBEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCHBEWARETHEJUBJUBBIRDANDSHUNTHEFRUMIOUSBANDERSNATCHHETOOKHISVORPALSWORDINHANDLONGTIMETHEMANXOMEFOEHESOUGHTSORESTEDHEBYTHETUMTUMTREEANDSTOODAWHILEINTHOUGHTANDASINUFFISHTHOUGHTHESTOODTHEJABBERWOCKWITHEYESOFFLAMECAMEWHIFFLINGTHROUGHTHETULGEYWOODANDBURBLEDASITCAMEONETWOONETWOANDTHROUGHANDTHROUGHTHEVORPALBLADEWENTSNICKERSNACKHELEFTITDEADANDWITHITSHEADHEWENTGALUMPHINGBACKANDHASTTHOUSLAINTHEJABBERWOCKCOMETOMYARMSMYBEAMISHBOYOFRABJOUSDAYCALLOOHCALLAYHECHORTLEDINHISJOYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIMBLEINTHEWABEALLMIMSYWERETHEBOROGOVESANDTHEMOMERATHSOUTGRABEITSEEMSVERYPRETTYSHESAIDWHENSHEHADFINISHEDITBUTITSRATHERHARDTOUNDERSTAND
zkl
var[const] uppercase=["A".."Z"].pump(String),
english_frequences=T( // A..Z
0.08167, 0.01492, 0.02782, 0.04253, 0.12702, 0.02228, 0.02015,
0.06094, 0.06966, 0.00153, 0.00772, 0.04025, 0.02406, 0.06749,
0.07507, 0.01929, 0.00095, 0.05987, 0.06327, 0.09056, 0.02758,
0.00978, 0.02360, 0.00150, 0.01974, 0.00074);
fcn vigenere_decrypt(target_freqs, input){ // ( (float,...), string)
nchars,ordA :=uppercase.len(),"A".toAsc();
sorted_targets:=target_freqs.sort();
frequency:='wrap(input){ // (n,n,n,n,...), n is ASCII index ("A"==65)
result:=uppercase.pump(List(),List.fp1(0)); // ( ("A",0),("B",0) ...)
foreach c in (input){ result[c - ordA][1] += 1 }
result // --> mutable list of mutable lists ( ("A",Int)...("Z",Int) )
};
correlation:='wrap(input){ // (n,n,n,n,...), n is ASCII index ("A"==65)
result,freq:=0.0, frequency(input);
freq.sort(fcn([(_,a)],[(_,b)]){ a<b }); // sort letters by frequency
foreach i,f in (freq.enumerate()){ result+=sorted_targets[i]*f[1] }
result // -->Float
};
cleaned:=input.toUpper().pump(List,uppercase.holds,Void.Filter,"toAsc");
best_len,best_corr := 0,-100.0;
# Assume that if there are less than 20 characters
# per column, the key's too long to guess
foreach i in ([2..cleaned.len()/20]){
pieces:=(i).pump(List,List.copy); // ( (),() ... )
foreach c in (cleaned){ pieces[__cWalker.idx%i].append(c) }
# The correlation seems to increase for smaller
# pieces/longer keys, so weigh against them a little
corr:=-0.5*i + pieces.apply(correlation).sum(0.0);
if(corr>best_corr) best_len,best_corr=i,corr;
}
if(best_len==0) return("Text is too short to analyze", "");
pieces:=best_len.pump(List,List.copy);
foreach c in (cleaned){ pieces[__cWalker.idx%best_len].append(c) }
key,freqs := "",pieces.apply(frequency);
foreach fr in (freqs){
fr.sort(fcn([(_,a)],[(_,b)]){ a>b }); // reverse sort by freq
m,max_corr := 0,0.0;
foreach j in (nchars){
corr,c := 0.0,ordA + j;
foreach frc in (fr){
d:=(frc[0].toAsc() - c + nchars) % nchars;
corr+=target_freqs[d]*frc[1];
if(corr>max_corr) m,max_corr=j,corr;
}
}
key+=(m + ordA).toChar();
}
cleaned.enumerate().apply('wrap([(i,c])){
( (c - (key[i%best_len]).toAsc() + nchars)%nchars + ordA ).toChar()
}).concat() :
T(key,_);
}
encryptedText:=
#<<<
"MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH
VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD
ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS
FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG
ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ
ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS
JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT
LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST
MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH
QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV
RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW
TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO
SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR
ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX
BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB
BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA
FWAML ZZRXJ EKAHV FASMU LVVUT TGK";
#<<<
key,decoded:=vigenere_decrypt(english_frequences,encryptedText);
println("Key:", key);
println("Decoded text:", decoded);
- Output:
Key:THECHESHIRECAT Decoded text:THISWASTHEPOEMTHATALICEREADJABBERWOCKYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIMBLEINTHEWABEALLMIMSYWERETHEBOROGOVESANDTHEMOMERATHSOUTGRABEBEWARETHEJABBERWOCKMYSONTHEJAWSTHATBITETHECLAWSTHATCATCHBEWARETHEJUBJUBBIRDANDSHUNTHEFRUMIOUSBANDERSNATCHHETOOKHISVORPALSWORDINHANDLONGTIMETHEMANXOMEFOEHESOUGHTSORESTEDHEBYTHETUMTUMTREEANDSTOODAWHILEINTHOUGHTANDASINUFFISHTHOUGHTHESTOODTHEJABBERWOCKWITHEYESOFFLAMECAMEWHIFFLINGTHROUGHTHETULGEYWOODANDBURBLEDASITCAMEONETWOONETWOANDTHROUGHANDTHROUGHTHEVORPALBLADEWENTSNICKERSNACKHELEFTITDEADANDWITHITSHEADHEWENTGALUMPHINGBACKANDHASTTHOUSLAINTHEJABBERWOCKCOMETOMYARMSMYBEAMISHBOYOFRABJOUSDAYCALLOOHCALLAYHECHORTLEDINHISJOYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIMBLEINTHEWABEALLMIMSYWERETHEBOROGOVESANDTHEMOMERATHSOUTGRABEITSEEMSVERYPRETTYSHESAIDWHENSHEHADFINISHEDITBUTITSRATHERHARDTOUNDERSTAND