Repository navigation
Expand file tree
/
Copy pathcryptanalysis.html
More file actions
1040 lines (953 loc) · 95.5 KB
/
Copy pathcryptanalysis.html
File metadata and controls
1040 lines (953 loc) · 95.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Cryptanalysis Techniques — The Cipher Museum</title>
<meta name="description" content="Learn how codebreakers crack ciphers. Frequency analysis, Kasiski examination, index of coincidence, and modern computational methods.">
<meta property="og:title" content="Cryptanalysis Techniques — The Cipher Museum">
<meta property="og:description" content="Learn how codebreakers crack ciphers. Frequency analysis, Kasiski examination, index of coincidence, and modern computational methods.">
<meta property="og:type" content="website">
<meta property="og:url" content="https://ciphermuseum.com/cryptanalysis.html">
<meta property="og:image" content="https://ciphermuseum.com/images/og/og-default.png">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="og:image:alt" content="The Cipher Museum — an interactive history of encryption">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="Cryptanalysis Techniques — The Cipher Museum">
<meta name="twitter:description" content="Learn how codebreakers crack ciphers. Frequency analysis, Kasiski examination, index of coincidence, and modern computational methods.">
<meta name="twitter:image" content="https://ciphermuseum.com/images/og/og-default.png">
<meta name="theme-color" content="#0a0a0f">
<link rel="canonical" href="https://ciphermuseum.com/cryptanalysis.html">
<link rel="icon" type="image/svg+xml" href="favicon.svg">
<link rel="manifest" href="manifest.webmanifest">
<link rel="stylesheet" href="css/museum.css">
<style>
.lab-grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(300px,1fr)); gap:1.25rem; margin-bottom:2rem; }
.technique-card {
background:var(--s2); border:1px solid var(--s4); border-radius:var(--rl); padding:1.5rem;
transition:all var(--trs); cursor:default;
display:flex; flex-direction:column;
}
.technique-card .figure { margin-top:auto; }
.technique-card .technique-breaks { margin-top:.75rem; }
.technique-card:hover { border-color:var(--red-b); background:var(--s3); }
.technique-num { font-family:var(--fm); font-size:.6rem; letter-spacing:.15em; color:var(--tx3); margin-bottom:.5rem; }
.technique-name { font-family:var(--fd); font-size:1.15rem; color:var(--tx); margin-bottom:.35rem; letter-spacing:.04em; }
.technique-by { font-family:var(--fm); font-size:.65rem; letter-spacing:.1em; color:var(--gold-dim); margin-bottom:.75rem; }
.technique-desc { font-size:.95rem; color:var(--tx2); line-height:1.75; margin-bottom:.75rem; }
.technique-breaks { display:flex; flex-wrap:wrap; gap:.35rem; }
.timeline { display:flex; flex-direction:column; gap:0; }
.tl-item { display:grid; grid-template-columns:80px 1fr; gap:1.5rem; }
.tl-year { font-family:var(--fm); font-size:.72rem; color:var(--gold-dim); text-align:right; padding-top:1.2rem; }
.tl-body { padding:1rem 0 1.5rem 1.5rem; border-left:1px solid var(--s4); position:relative; }
.tl-body::before { content:''; position:absolute; left:-4px; top:1.3rem; width:7px; height:7px; border-radius:50%; background:var(--gold-dim); }
.tl-title { font-family:var(--fd); font-size:1.05rem; color:var(--tx); margin-bottom:.25rem; }
.tl-person { font-family:var(--fm); font-size:.62rem; letter-spacing:.1em; color:var(--gold); margin-bottom:.4rem; }
.tl-technique { font-family:var(--fm); font-size:.62rem; letter-spacing:.08em; color:var(--tx3); margin-bottom:.5rem; }
.tl-impact { font-size:.92rem; color:var(--tx2); line-height:1.7; }
.tl-item.legendary .tl-body::before { background:var(--gold); width:9px; height:9px; left:-5px; }
.tl-item.legendary .tl-body { border-color:var(--gold-b); }
/* Interactive challenge tabs */
.lab-tabs { display:flex; gap:0; background:var(--s3); border-radius:var(--r); padding:3px; margin-bottom:2rem; flex-wrap:wrap; }
.lab-tab { flex:1; min-width:140px; padding:.55rem 1rem; border:none; background:none; font-family:var(--fd); font-size:.75rem; letter-spacing:.1em; text-transform:uppercase; color:var(--tx3); cursor:pointer; border-radius:3px; transition:all var(--tr); }
.lab-tab.active { background:var(--gold); color:var(--black); font-weight:700; }
.lab-panel { display:none; } .lab-panel.active { display:block; }
.ic-display { font-family:var(--fm); font-size:2rem; color:var(--gold); text-align:center; padding:1rem; }
.ic-label { font-family:var(--fm); font-size:.62rem; letter-spacing:.15em; text-transform:uppercase; color:var(--tx3); text-align:center; }
.ic-bar-wrap { height:8px; background:var(--s4); border-radius:4px; margin:1rem 0; overflow:hidden; }
.ic-bar { height:100%; border-radius:4px; background:linear-gradient(90deg,var(--red),var(--gold),var(--green)); transition:width .4s ease; }
.kasiski-hits { margin-top:1rem; display:flex; flex-direction:column; gap:.4rem; max-height:200px; overflow-y:auto; }
.kasiski-hit { font-family:var(--fm); font-size:.75rem; background:var(--s3); border:1px solid var(--gold-b); border-radius:var(--r); padding:.5rem .75rem; color:var(--gold-lt); }
/* Responsive timeline */
@media(max-width:768px){
#timeline{grid-template-columns:1fr !important}
#timeline .timeline:first-child{padding-right:0}
#timeline .timeline:last-child{padding-left:0}
.lab-grid.reveal{opacity:1 !important;transform:none !important}
.lab-grid{grid-template-columns:1fr !important}
}
@media(max-width:600px){
.lab-tab{min-width:90px;font-size:.62rem;padding:.45rem .5rem}
}
/* Tab icons */
.lab-tab::before{margin-right:.4rem;font-size:.7rem}
#tab-btn-freq::before{content:'📊'}
#tab-btn-kasiski::before{content:'🔍'}
#tab-btn-ioc::before{content:'📐'}
#tab-btn-crib::before{content:'🔑'}
#tab-btn-kpa::before{content:'📄'}
#tab-btn-hillclimb::before{content:'⛰️'}
#tab-btn-anneal::before{content:'🧬'}
/* Lab textarea for older inline-styled inputs */
.lab-textarea{width:100%;padding:.8rem;background:var(--s3);border:1px solid var(--s4);border-radius:var(--r-sm);color:var(--tx);font-family:var(--fm);font-size:.82rem;resize:vertical}
.lab-textarea:focus{border-color:var(--gold-dim);outline:none}
.lab-result{margin-top:1.25rem;padding:1rem;background:var(--s2);border:1px solid var(--s4);border-radius:var(--r-sm);font-family:var(--fm);font-size:.82rem;color:var(--tx2);line-height:1.8;min-height:3rem}
.caesar-landscape-table th { white-space: nowrap; }
.caesar-landscape-table th, .caesar-landscape-table td { padding: .4rem .75rem .4rem 0; vertical-align: top; }
.caesar-landscape-table th:last-child, .caesar-landscape-table td:last-child { padding-right: 0; }
</style>
</head>
<body>
<a class="skip-link" href="#main-content">Skip to main content</a>
<nav class="museum-nav" aria-label="Primary">
<div class="nav-inner">
<a href="index.html" class="nav-logo">
<svg class="nav-logo-icon" viewBox="0 0 32 32" fill="none">
<circle cx="16" cy="16" r="14" stroke="currentColor" stroke-width="1.5"/>
<circle cx="16" cy="16" r="8" stroke="currentColor" stroke-width="1"/>
<circle cx="16" cy="16" r="2" fill="currentColor"/>
<line x1="16" y1="2" x2="16" y2="6" stroke="currentColor" stroke-width="1.5"/>
<line x1="16" y1="26" x2="16" y2="30" stroke="currentColor" stroke-width="1.5"/>
<line x1="2" y1="16" x2="6" y2="16" stroke="currentColor" stroke-width="1.5"/>
<line x1="26" y1="16" x2="30" y2="16" stroke="currentColor" stroke-width="1.5"/>
</svg>
<span class="nav-logo-text">The Cipher Museum</span>
</a>
<ul class="nav-links">
<li><a href="museum-map.html">Explore</a></li>
<li><a href="timeline.html">Timeline</a></li>
<li><a href="learn.html">Learn</a></li>
<li><a href="cipher-corpus.html">Cipher Corpus</a></li>
<li><a href="cryptanalysis.html">Cryptanalysis</a></li>
<li><a href="challenges.html">Challenges</a></li>
<li><a href="lab/workbench.html">Lab</a></li>
<li><a href="search.html">Search</a></li>
</ul>
</div>
</nav>
<main id="main-content" tabindex="-1">
<div class="page-hero">
<div class="breadcrumb"><a href="index.html">Entrance</a><span>›</span>Cryptanalysis Techniques</div>
<span class="page-eyebrow">Special Exhibit · The Science of Breaking Codes</span>
<h1 class="page-title">Cryptanalysis Techniques</h1>
<p class="page-tagline">"To know how to defend, you must first know how to attack."</p>
<p class="page-desc">Ten approaches to classical cryptanalysis, with seven hands-on demonstrations. Success depends on the cipher, evidence, language, key and message length; no technique here defeats a correctly used one-time pad.</p>
<a href="lab/workbench.html" style="display:inline-block;margin-top:1rem;font-family:var(--fd);font-size:.82rem;letter-spacing:.1em;text-transform:uppercase;color:var(--gold);text-decoration:none;padding:.6rem 1.4rem;border:1px solid var(--gold-b);border-radius:var(--r);position:relative;z-index:1;transition:all .25s ease">Open Codebreaker's Workbench →</a>
<a href="cipher-detective.html" style="display:inline-block;margin-top:1rem;margin-left:.75rem;font-family:var(--fd);font-size:.82rem;letter-spacing:.1em;text-transform:uppercase;color:var(--gold);text-decoration:none;padding:.6rem 1.4rem;border:1px solid var(--gold-b);border-radius:var(--r);position:relative;z-index:1;transition:all .25s ease">Open Cipher Detective →</a>
</div>
<!-- ═══ MAIN CONTENT ══════════════════════════════════ -->
<div class="section">
<!-- 7 Techniques -->
<div class="section-head">
<span class="section-eyebrow">The Toolkit</span>
<h2 class="section-title">10 Techniques That Break Classical Ciphers</h2>
</div>
<div class="lab-grid reveal">
<div class="technique-card">
<div class="technique-num">Technique 01</div>
<div class="technique-name">Frequency Analysis</div>
<div class="technique-by">Al-Kindi · Baghdad · ~850 AD</div>
<p class="technique-desc">Long English samples often have frequencies near E=12.7%, T=9.1%, A=8.2%, with variation by text. A fixed one-to-one substitution preserves the sample’s counts while relabeling letters. Compare counts and sequences to propose mappings; frequency alone does not guarantee key recovery. Homophonic and syllabic systems need additional analysis.</p>
<figure class="figure" style="margin-top:1rem;">
<svg viewBox="0 0 520 220" style="max-width:560px;width:100%;height:auto;display:block;background:#1a1a2e;border:1px solid #2e2e3a;border-radius:6px;" role="img" aria-label="Bar chart of English letter frequencies, E highest at 12.7 percent"><line x1="24" y1="196" x2="506" y2="196" stroke="#e8d5a3" stroke-width="1.2"/><rect x="28.0" y="101.7" width="12" height="94.3" fill="#c8b273"/><text x="34.0" y="210" style="font-family:ui-monospace,monospace;font-size:9px;fill:#e8d5a3;text-anchor:middle">A</text><text x="34.0" y="95.7" style="font-family:ui-monospace,monospace;font-size:8px;fill:#c8b273;text-anchor:middle">8.2</text><rect x="46.4" y="178.8" width="12" height="17.2" fill="#8b7355"/><text x="52.4" y="210" style="font-family:ui-monospace,monospace;font-size:9px;fill:#e8d5a3;text-anchor:middle">B</text><rect x="64.8" y="163.8" width="12" height="32.2" fill="#8b7355"/><text x="70.8" y="210" style="font-family:ui-monospace,monospace;font-size:9px;fill:#e8d5a3;text-anchor:middle">C</text><rect x="83.2" y="146.6" width="12" height="49.4" fill="#8b7355"/><text x="89.19999999999999" y="210" style="font-family:ui-monospace,monospace;font-size:9px;fill:#e8d5a3;text-anchor:middle">D</text><rect x="101.6" y="50.0" width="12" height="146.0" fill="#d4af37"/><text x="107.6" y="210" style="font-family:ui-monospace,monospace;font-size:9px;fill:#e8d5a3;text-anchor:middle">E</text><text x="107.6" y="43.95000000000002" style="font-family:ui-monospace,monospace;font-size:8px;fill:#d4af37;text-anchor:middle">12.7</text><rect x="120.0" y="170.7" width="12" height="25.3" fill="#8b7355"/><text x="126.0" y="210" style="font-family:ui-monospace,monospace;font-size:9px;fill:#e8d5a3;text-anchor:middle">F</text><rect x="138.4" y="173.0" width="12" height="23.0" fill="#8b7355"/><text x="144.39999999999998" y="210" style="font-family:ui-monospace,monospace;font-size:9px;fill:#e8d5a3;text-anchor:middle">G</text><rect x="156.8" y="125.9" width="12" height="70.1" fill="#c8b273"/><text x="162.79999999999998" y="210" style="font-family:ui-monospace,monospace;font-size:9px;fill:#e8d5a3;text-anchor:middle">H</text><text x="162.79999999999998" y="119.85000000000001" style="font-family:ui-monospace,monospace;font-size:8px;fill:#c8b273;text-anchor:middle">6.1</text><rect x="175.2" y="115.5" width="12" height="80.5" fill="#c8b273"/><text x="181.2" y="210" style="font-family:ui-monospace,monospace;font-size:9px;fill:#e8d5a3;text-anchor:middle">I</text><text x="181.2" y="109.5" style="font-family:ui-monospace,monospace;font-size:8px;fill:#c8b273;text-anchor:middle">7.0</text><rect x="193.6" y="194.3" width="12" height="1.7" fill="#8b7355"/><text x="199.6" y="210" style="font-family:ui-monospace,monospace;font-size:9px;fill:#e8d5a3;text-anchor:middle">J</text><rect x="212.0" y="186.8" width="12" height="9.2" fill="#8b7355"/><text x="218.0" y="210" style="font-family:ui-monospace,monospace;font-size:9px;fill:#e8d5a3;text-anchor:middle">K</text><rect x="230.4" y="150.0" width="12" height="46.0" fill="#8b7355"/><text x="236.39999999999998" y="210" style="font-family:ui-monospace,monospace;font-size:9px;fill:#e8d5a3;text-anchor:middle">L</text><rect x="248.8" y="168.4" width="12" height="27.6" fill="#8b7355"/><text x="254.79999999999998" y="210" style="font-family:ui-monospace,monospace;font-size:9px;fill:#e8d5a3;text-anchor:middle">M</text><rect x="267.2" y="119.0" width="12" height="77.0" fill="#c8b273"/><text x="273.2" y="210" style="font-family:ui-monospace,monospace;font-size:9px;fill:#e8d5a3;text-anchor:middle">N</text><text x="273.2" y="112.95" style="font-family:ui-monospace,monospace;font-size:8px;fill:#c8b273;text-anchor:middle">6.7</text><rect x="285.6" y="109.8" width="12" height="86.2" fill="#c8b273"/><text x="291.59999999999997" y="210" style="font-family:ui-monospace,monospace;font-size:9px;fill:#e8d5a3;text-anchor:middle">O</text><text x="291.59999999999997" y="103.75" style="font-family:ui-monospace,monospace;font-size:8px;fill:#c8b273;text-anchor:middle">7.5</text><rect x="304.0" y="174.2" width="12" height="21.8" fill="#8b7355"/><text x="310.0" y="210" style="font-family:ui-monospace,monospace;font-size:9px;fill:#e8d5a3;text-anchor:middle">P</text><rect x="322.4" y="194.8" width="12" height="1.2" fill="#8b7355"/><text x="328.4" y="210" style="font-family:ui-monospace,monospace;font-size:9px;fill:#e8d5a3;text-anchor:middle">Q</text><rect x="340.8" y="127.0" width="12" height="69.0" fill="#c8b273"/><text x="346.79999999999995" y="210" style="font-family:ui-monospace,monospace;font-size:9px;fill:#e8d5a3;text-anchor:middle">R</text><text x="346.79999999999995" y="121.0" style="font-family:ui-monospace,monospace;font-size:8px;fill:#c8b273;text-anchor:middle">6.0</text><rect x="359.2" y="123.5" width="12" height="72.5" fill="#c8b273"/><text x="365.2" y="210" style="font-family:ui-monospace,monospace;font-size:9px;fill:#e8d5a3;text-anchor:middle">S</text><text x="365.2" y="117.55" style="font-family:ui-monospace,monospace;font-size:8px;fill:#c8b273;text-anchor:middle">6.3</text><rect x="377.6" y="91.4" width="12" height="104.6" fill="#c8b273"/><text x="383.59999999999997" y="210" style="font-family:ui-monospace,monospace;font-size:9px;fill:#e8d5a3;text-anchor:middle">T</text><text x="383.59999999999997" y="85.35000000000001" style="font-family:ui-monospace,monospace;font-size:8px;fill:#c8b273;text-anchor:middle">9.1</text><rect x="396.0" y="163.8" width="12" height="32.2" fill="#8b7355"/><text x="402.0" y="210" style="font-family:ui-monospace,monospace;font-size:9px;fill:#e8d5a3;text-anchor:middle">U</text><rect x="414.4" y="184.5" width="12" height="11.5" fill="#8b7355"/><text x="420.4" y="210" style="font-family:ui-monospace,monospace;font-size:9px;fill:#e8d5a3;text-anchor:middle">V</text><rect x="432.8" y="168.4" width="12" height="27.6" fill="#8b7355"/><text x="438.79999999999995" y="210" style="font-family:ui-monospace,monospace;font-size:9px;fill:#e8d5a3;text-anchor:middle">W</text><rect x="451.2" y="194.3" width="12" height="1.7" fill="#8b7355"/><text x="457.2" y="210" style="font-family:ui-monospace,monospace;font-size:9px;fill:#e8d5a3;text-anchor:middle">X</text><rect x="469.6" y="173.0" width="12" height="23.0" fill="#8b7355"/><text x="475.59999999999997" y="210" style="font-family:ui-monospace,monospace;font-size:9px;fill:#e8d5a3;text-anchor:middle">Y</text><rect x="488.0" y="195.2" width="12" height="0.8" fill="#8b7355"/><text x="493.99999999999994" y="210" style="font-family:ui-monospace,monospace;font-size:9px;fill:#e8d5a3;text-anchor:middle">Z</text><text x="28" y="20" style="font-family:ui-monospace,monospace;font-size:10px;fill:#8b7355;text-anchor:start">% frequency in English text</text><text x="88" y="46" style="font-family:ui-monospace,monospace;font-size:9.5px;fill:#555;text-anchor:start">fixed substitution preserves sample counts; symbols change</text></svg>
<figcaption class="figure-caption">English letter frequency distribution — the foundation of frequency analysis attacks.</figcaption>
</figure>
<div class="technique-breaks">
<a href="ciphers/caesar.html" class="badge sec-broken" style="text-decoration:none;">Caesar</a>
<a href="ciphers/monoalphabetic.html" class="badge sec-broken" style="text-decoration:none;">Monoalphabetic</a>
<a href="ciphers/homophonic.html" class="badge sec-broken" style="text-decoration:none;">Homophonic</a>
<a href="ciphers/great-cipher.html" class="badge sec-broken" style="text-decoration:none;">Great Cipher</a>
</div>
</div>
<div class="technique-card">
<div class="technique-num">Technique 02</div>
<div class="technique-name">Kasiski Examination</div>
<div class="technique-by">Friedrich Kasiski · 1863</div>
<p class="technique-desc">In repeating-key Vigenère, the same plaintext at the same key phase produces the same ciphertext. Repeated ciphertext fragments can suggest key periods through factors of their spacing. Accidental repeats also occur, so candidate periods need further tests.</p>
<figure class="figure" style="margin-top:1rem;">
<svg viewBox="0 0 520 175" style="max-width:560px;width:100%;height:auto;display:block;background:#1a1a2e;border:1px solid #2e2e3a;border-radius:6px;" role="img" aria-label="Kasiski illustration: matching fragments at distance 9 suggest testing factors of 9"><text x="40" y="40" style="font-family:ui-monospace,monospace;font-size:13px;fill:#8b7355;text-anchor:middle">T</text><text x="40" y="62" style="font-family:ui-monospace,monospace;font-size:11px;fill:#555;text-anchor:middle">R</text><rect x="29" y="76" width="22" height="24" fill="none" stroke="#d4af37" stroke-width="1.4" rx="3"/><text x="40" y="93" style="font-family:ui-monospace,monospace;font-size:14px;fill:#d4af37;text-anchor:middle;font-weight:700">K</text><text x="74" y="40" style="font-family:ui-monospace,monospace;font-size:13px;fill:#8b7355;text-anchor:middle">O</text><text x="74" y="62" style="font-family:ui-monospace,monospace;font-size:11px;fill:#555;text-anchor:middle">U</text><rect x="63" y="76" width="22" height="24" fill="none" stroke="#d4af37" stroke-width="1.4" rx="3"/><text x="74" y="93" style="font-family:ui-monospace,monospace;font-size:14px;fill:#d4af37;text-anchor:middle;font-weight:700">I</text><text x="108" y="40" style="font-family:ui-monospace,monospace;font-size:13px;fill:#8b7355;text-anchor:middle">B</text><text x="108" y="62" style="font-family:ui-monospace,monospace;font-size:11px;fill:#555;text-anchor:middle">N</text><rect x="97" y="76" width="22" height="24" fill="none" stroke="#d4af37" stroke-width="1.4" rx="3"/><text x="108" y="93" style="font-family:ui-monospace,monospace;font-size:14px;fill:#d4af37;text-anchor:middle;font-weight:700">O</text><text x="142" y="40" style="font-family:ui-monospace,monospace;font-size:13px;fill:#8b7355;text-anchor:middle">E</text><text x="142" y="62" style="font-family:ui-monospace,monospace;font-size:11px;fill:#555;text-anchor:middle">R</text><rect x="131" y="76" width="22" height="24" fill="none" stroke="#d4af37" stroke-width="1.4" rx="3"/><text x="142" y="93" style="font-family:ui-monospace,monospace;font-size:14px;fill:#d4af37;text-anchor:middle;font-weight:700">V</text><text x="176" y="40" style="font-family:ui-monospace,monospace;font-size:13px;fill:#8b7355;text-anchor:middle">O</text><text x="176" y="62" style="font-family:ui-monospace,monospace;font-size:11px;fill:#555;text-anchor:middle">U</text><text x="176" y="93" style="font-family:ui-monospace,monospace;font-size:14px;fill:#e8d5a3;text-anchor:middle;font-weight:700">I</text><text x="210" y="40" style="font-family:ui-monospace,monospace;font-size:13px;fill:#8b7355;text-anchor:middle">R</text><text x="210" y="62" style="font-family:ui-monospace,monospace;font-size:11px;fill:#555;text-anchor:middle">N</text><text x="210" y="93" style="font-family:ui-monospace,monospace;font-size:14px;fill:#e8d5a3;text-anchor:middle;font-weight:700">E</text><text x="244" y="40" style="font-family:ui-monospace,monospace;font-size:13px;fill:#8b7355;text-anchor:middle">N</text><text x="244" y="62" style="font-family:ui-monospace,monospace;font-size:11px;fill:#555;text-anchor:middle">R</text><text x="244" y="93" style="font-family:ui-monospace,monospace;font-size:14px;fill:#e8d5a3;text-anchor:middle;font-weight:700">E</text><text x="278" y="40" style="font-family:ui-monospace,monospace;font-size:13px;fill:#8b7355;text-anchor:middle">O</text><text x="278" y="62" style="font-family:ui-monospace,monospace;font-size:11px;fill:#555;text-anchor:middle">U</text><text x="278" y="93" style="font-family:ui-monospace,monospace;font-size:14px;fill:#e8d5a3;text-anchor:middle;font-weight:700">I</text><text x="312" y="40" style="font-family:ui-monospace,monospace;font-size:13px;fill:#8b7355;text-anchor:middle">T</text><text x="312" y="62" style="font-family:ui-monospace,monospace;font-size:11px;fill:#555;text-anchor:middle">N</text><text x="312" y="93" style="font-family:ui-monospace,monospace;font-size:14px;fill:#e8d5a3;text-anchor:middle;font-weight:700">G</text><text x="346" y="40" style="font-family:ui-monospace,monospace;font-size:13px;fill:#8b7355;text-anchor:middle">T</text><text x="346" y="62" style="font-family:ui-monospace,monospace;font-size:11px;fill:#555;text-anchor:middle">R</text><rect x="335" y="76" width="22" height="24" fill="none" stroke="#d4af37" stroke-width="1.4" rx="3"/><text x="346" y="93" style="font-family:ui-monospace,monospace;font-size:14px;fill:#d4af37;text-anchor:middle;font-weight:700">K</text><text x="380" y="40" style="font-family:ui-monospace,monospace;font-size:13px;fill:#8b7355;text-anchor:middle">O</text><text x="380" y="62" style="font-family:ui-monospace,monospace;font-size:11px;fill:#555;text-anchor:middle">U</text><rect x="369" y="76" width="22" height="24" fill="none" stroke="#d4af37" stroke-width="1.4" rx="3"/><text x="380" y="93" style="font-family:ui-monospace,monospace;font-size:14px;fill:#d4af37;text-anchor:middle;font-weight:700">I</text><text x="414" y="40" style="font-family:ui-monospace,monospace;font-size:13px;fill:#8b7355;text-anchor:middle">B</text><text x="414" y="62" style="font-family:ui-monospace,monospace;font-size:11px;fill:#555;text-anchor:middle">N</text><rect x="403" y="76" width="22" height="24" fill="none" stroke="#d4af37" stroke-width="1.4" rx="3"/><text x="414" y="93" style="font-family:ui-monospace,monospace;font-size:14px;fill:#d4af37;text-anchor:middle;font-weight:700">O</text><text x="448" y="40" style="font-family:ui-monospace,monospace;font-size:13px;fill:#8b7355;text-anchor:middle">E</text><text x="448" y="62" style="font-family:ui-monospace,monospace;font-size:11px;fill:#555;text-anchor:middle">R</text><rect x="437" y="76" width="22" height="24" fill="none" stroke="#d4af37" stroke-width="1.4" rx="3"/><text x="448" y="93" style="font-family:ui-monospace,monospace;font-size:14px;fill:#d4af37;text-anchor:middle;font-weight:700">V</text><text x="18" y="40" style="font-family:ui-monospace,monospace;font-size:9px;fill:#8b7355;text-anchor:start">plain</text><text x="18" y="62" style="font-family:ui-monospace,monospace;font-size:9px;fill:#555;text-anchor:start">key</text><text x="14" y="93" style="font-family:ui-monospace,monospace;font-size:9px;fill:#c8b273;text-anchor:start">cipher</text><path d="M91 108 v14 h306 v-14" fill="none" stroke="#c8b273" stroke-width="1.2"/><text x="244" y="138" style="font-family:ui-monospace,monospace;font-size:10.5px;fill:#8b7355;text-anchor:middle">same plaintext + same key phase → same ciphertext</text><text x="244" y="158" style="font-family:ui-monospace,monospace;font-size:12px;fill:#d4af37;text-anchor:middle;font-weight:700">distance 9 → candidate periods 1, 3, 9; repeats may be accidental</text></svg>
<figcaption class="figure-caption">Kasiski examination — repeated ciphertext sequences suggest candidate Vigenère periods.</figcaption>
</figure>
<div class="technique-breaks">
<a href="ciphers/vigenere.html" class="badge sec-broken" style="text-decoration:none;">Vigenère</a>
<a href="ciphers/beaufort.html" class="badge sec-broken" style="text-decoration:none;">Beaufort</a>
<a href="ciphers/gronsfeld.html" class="badge sec-broken" style="text-decoration:none;">Gronsfeld</a>
</div>
</div>
<div class="technique-card">
<div class="technique-num">Technique 03</div>
<div class="technique-name">Index of Coincidence</div>
<div class="technique-by">William Friedman · published 1922</div>
<p class="technique-desc">Measures the probability that two distinct sampled positions contain the same letter. Long English samples often lie near 0.066; independent uniform A–Z text has expected IC 1/26 ≈ 0.0385. These are reference values, not bounds: an observed IC can range from 0 to 1. Coincidence statistics can suggest repeating-key periods, but do not identify a cipher by themselves.</p>
<figure class="figure" style="margin-top:1rem;">
<svg viewBox="0 0 520 220" style="max-width:560px;width:100%;height:auto;display:block;background:#1a1a2e;border:1px solid #2e2e3a;border-radius:6px;" role="img" aria-label="Index of coincidence: English 0.0667 vs random 0.0385, and per-key-length peaks at multiples of 3"><line x1="30" y1="170" x2="200" y2="170" stroke="#e8d5a3" stroke-width="1.2"/><rect x="55" y="43" width="40" height="127" fill="#d4af37"/><text x="75" y="39.27000000000001" style="font-family:ui-monospace,monospace;font-size:10px;fill:#d4af37;text-anchor:middle">0.0667</text><text x="75" y="184" style="font-family:ui-monospace,monospace;font-size:10px;fill:#e8d5a3;text-anchor:middle">English</text><rect x="135" y="97" width="40" height="73" fill="#8b7355"/><text x="155" y="92.85" style="font-family:ui-monospace,monospace;font-size:10px;fill:#8b7355;text-anchor:middle">0.0385</text><text x="155" y="184" style="font-family:ui-monospace,monospace;font-size:10px;fill:#e8d5a3;text-anchor:middle">random</text><text x="115" y="24" style="font-family:ui-monospace,monospace;font-size:10px;fill:#8b7355;text-anchor:middle">reference IC</text><line x1="250" y1="170" x2="500" y2="170" stroke="#e8d5a3" stroke-width="1.2"/><rect x="262" y="84" width="18" height="86" fill="#555"/><text x="271" y="184" style="font-family:ui-monospace,monospace;font-size:10px;fill:#e8d5a3;text-anchor:middle">1</text><rect x="291" y="83" width="18" height="87" fill="#555"/><text x="300" y="184" style="font-family:ui-monospace,monospace;font-size:10px;fill:#e8d5a3;text-anchor:middle">2</text><rect x="320" y="46" width="18" height="124" fill="#d4af37"/><text x="329" y="184" style="font-family:ui-monospace,monospace;font-size:10px;fill:#e8d5a3;text-anchor:middle">3</text><rect x="349" y="86" width="18" height="84" fill="#555"/><text x="358" y="184" style="font-family:ui-monospace,monospace;font-size:10px;fill:#e8d5a3;text-anchor:middle">4</text><rect x="378" y="81" width="18" height="89" fill="#555"/><text x="387" y="184" style="font-family:ui-monospace,monospace;font-size:10px;fill:#e8d5a3;text-anchor:middle">5</text><rect x="407" y="48" width="18" height="122" fill="#d4af37"/><text x="416" y="184" style="font-family:ui-monospace,monospace;font-size:10px;fill:#e8d5a3;text-anchor:middle">6</text><rect x="436" y="84" width="18" height="86" fill="#555"/><text x="445" y="184" style="font-family:ui-monospace,monospace;font-size:10px;fill:#e8d5a3;text-anchor:middle">7</text><rect x="465" y="83" width="18" height="87" fill="#555"/><text x="474" y="184" style="font-family:ui-monospace,monospace;font-size:10px;fill:#e8d5a3;text-anchor:middle">8</text><text x="375" y="24" style="font-family:ui-monospace,monospace;font-size:10px;fill:#8b7355;text-anchor:middle">IC by assumed key length</text><text x="375" y="204" style="font-family:ui-monospace,monospace;font-size:10.5px;fill:#c8b273;text-anchor:middle">illustrative peaks at 3, 6 — test a period of 3</text><text x="115" y="204" style="font-family:ui-monospace,monospace;font-size:9.5px;fill:#555;text-anchor:middle">reference value, not proof</text></svg>
<figcaption class="figure-caption">Index of Coincidence — a statistical clue for investigating cipher families and repeating-key periods.</figcaption>
</figure>
<div class="technique-breaks">
<a href="ciphers/vigenere.html" class="badge sec-broken" style="text-decoration:none;">Vigenère</a>
<a href="ciphers/running-key.html" class="badge sec-broken" style="text-decoration:none;">Running Key</a>
<span class="badge sec-broken">Polyalphabetic</span>
</div>
</div>
<div class="technique-card">
<div class="technique-num">Technique 04</div>
<div class="technique-name">Crib-Based Cryptanalysis</div>
<div class="technique-by">Polish Mathematicians · WWII Bletchley</div>
<p class="technique-desc">Cribs are hypotheses about plaintext, suggested by message formats, context or repeated phrases. Against Enigma, analysts tested those hypotheses against machine constraints and additional traffic. Known plaintext is an attack model that modern encryption must withstand; it is not automatically a fatal weakness.</p>
<figure class="figure" style="margin-top:1rem;">
<svg viewBox="0 0 520 200" style="max-width:560px;width:100%;height:auto;display:block;background:#1a1a2e;border:1px solid #2e2e3a;border-radius:6px;" role="img" aria-label="Crib sliding: offset 0 rejected because T aligns with T; offset 2 is a legal alignment"><text x="70" y="32" style="font-family:ui-monospace,monospace;font-size:13px;fill:#e8d5a3;text-anchor:middle">J</text><text x="100" y="32" style="font-family:ui-monospace,monospace;font-size:13px;fill:#e8d5a3;text-anchor:middle">X</text><text x="130" y="32" style="font-family:ui-monospace,monospace;font-size:13px;fill:#e8d5a3;text-anchor:middle">A</text><text x="160" y="32" style="font-family:ui-monospace,monospace;font-size:13px;fill:#e8d5a3;text-anchor:middle">T</text><text x="190" y="32" style="font-family:ui-monospace,monospace;font-size:13px;fill:#e8d5a3;text-anchor:middle">Q</text><text x="220" y="32" style="font-family:ui-monospace,monospace;font-size:13px;fill:#e8d5a3;text-anchor:middle">B</text><text x="250" y="32" style="font-family:ui-monospace,monospace;font-size:13px;fill:#e8d5a3;text-anchor:middle">G</text><text x="280" y="32" style="font-family:ui-monospace,monospace;font-size:13px;fill:#e8d5a3;text-anchor:middle">G</text><text x="310" y="32" style="font-family:ui-monospace,monospace;font-size:13px;fill:#e8d5a3;text-anchor:middle">Y</text><text x="340" y="32" style="font-family:ui-monospace,monospace;font-size:13px;fill:#e8d5a3;text-anchor:middle">W</text><text x="370" y="32" style="font-family:ui-monospace,monospace;font-size:13px;fill:#e8d5a3;text-anchor:middle">C</text><text x="400" y="32" style="font-family:ui-monospace,monospace;font-size:13px;fill:#e8d5a3;text-anchor:middle">R</text><text x="430" y="32" style="font-family:ui-monospace,monospace;font-size:13px;fill:#e8d5a3;text-anchor:middle">Y</text><text x="460" y="32" style="font-family:ui-monospace,monospace;font-size:13px;fill:#e8d5a3;text-anchor:middle">B</text><text x="30" y="32" style="font-family:ui-monospace,monospace;font-size:9px;fill:#8b7355;text-anchor:start">cipher</text><rect x="59" y="60" width="22" height="20" fill="none" stroke="#c8b273" stroke-width="1.3" rx="3"/><text x="70" y="74" style="font-family:ui-monospace,monospace;font-size:12px;fill:#c8b273;text-anchor:middle">W</text><rect x="89" y="60" width="22" height="20" fill="none" stroke="#c8b273" stroke-width="1.3" rx="3"/><text x="100" y="74" style="font-family:ui-monospace,monospace;font-size:12px;fill:#c8b273;text-anchor:middle">E</text><rect x="119" y="60" width="22" height="20" fill="none" stroke="#c8b273" stroke-width="1.3" rx="3"/><text x="130" y="74" style="font-family:ui-monospace,monospace;font-size:12px;fill:#c8b273;text-anchor:middle">T</text><rect x="149" y="60" width="22" height="20" fill="none" stroke="#C85A5A" stroke-width="1.3" rx="3"/><text x="160" y="74" style="font-family:ui-monospace,monospace;font-size:12px;fill:#C85A5A;text-anchor:middle">T</text><text x="160" y="55" style="font-family:ui-monospace,monospace;font-size:8.5px;fill:#C85A5A;text-anchor:middle">T = T</text><rect x="179" y="60" width="22" height="20" fill="none" stroke="#c8b273" stroke-width="1.3" rx="3"/><text x="190" y="74" style="font-family:ui-monospace,monospace;font-size:12px;fill:#c8b273;text-anchor:middle">E</text><rect x="209" y="60" width="22" height="20" fill="none" stroke="#c8b273" stroke-width="1.3" rx="3"/><text x="220" y="74" style="font-family:ui-monospace,monospace;font-size:12px;fill:#c8b273;text-anchor:middle">R</text><text x="260" y="100" style="font-family:ui-monospace,monospace;font-size:10px;fill:#C85A5A;text-anchor:middle">✗ offset 0 rejected — T would encrypt to itself, and Enigma never does</text><rect x="119" y="118" width="22" height="20" fill="none" stroke="#c8b273" stroke-width="1.3" rx="3"/><text x="130" y="132" style="font-family:ui-monospace,monospace;font-size:12px;fill:#c8b273;text-anchor:middle">W</text><rect x="149" y="118" width="22" height="20" fill="none" stroke="#c8b273" stroke-width="1.3" rx="3"/><text x="160" y="132" style="font-family:ui-monospace,monospace;font-size:12px;fill:#c8b273;text-anchor:middle">E</text><rect x="179" y="118" width="22" height="20" fill="none" stroke="#c8b273" stroke-width="1.3" rx="3"/><text x="190" y="132" style="font-family:ui-monospace,monospace;font-size:12px;fill:#c8b273;text-anchor:middle">T</text><rect x="209" y="118" width="22" height="20" fill="none" stroke="#c8b273" stroke-width="1.3" rx="3"/><text x="220" y="132" style="font-family:ui-monospace,monospace;font-size:12px;fill:#c8b273;text-anchor:middle">T</text><rect x="239" y="118" width="22" height="20" fill="none" stroke="#c8b273" stroke-width="1.3" rx="3"/><text x="250" y="132" style="font-family:ui-monospace,monospace;font-size:12px;fill:#c8b273;text-anchor:middle">E</text><rect x="269" y="118" width="22" height="20" fill="none" stroke="#c8b273" stroke-width="1.3" rx="3"/><text x="280" y="132" style="font-family:ui-monospace,monospace;font-size:12px;fill:#c8b273;text-anchor:middle">R</text><text x="260" y="158" style="font-family:ui-monospace,monospace;font-size:10px;fill:#c8b273;text-anchor:middle">✓ offset 2 survives — no letter meets itself; a legal alignment</text><text x="260" y="184" style="font-family:ui-monospace,monospace;font-size:10px;fill:#8b7355;text-anchor:middle">each surviving offset becomes a bombe menu to test</text></svg>
<figcaption class="figure-caption">Crib-based cryptanalysis — exploiting predictable message headers and standard phrases to break machine ciphers.</figcaption>
</figure>
<div class="technique-breaks">
<a href="ciphers/enigma.html" class="badge sec-broken" style="text-decoration:none;">Enigma</a>
<a href="ciphers/lorenz.html" class="badge sec-broken" style="text-decoration:none;">Lorenz</a>
<a href="ciphers/vigenere.html" class="badge sec-broken" style="text-decoration:none;">Vigenère</a>
</div>
</div>
<div class="technique-card">
<div class="technique-num">Technique 05</div>
<div class="technique-name">Known Plaintext Attack</div>
<div class="technique-by">Universal · Classical through Modern</div>
<p class="technique-desc">Known plaintext can expose a classical cipher's key. For a 2×2 Hill cipher, two matched plaintext/ciphertext <em>blocks</em> suffice when the plaintext-block matrix P is invertible modulo 26: gcd(det(P), 26) = 1. Otherwise, more observations or other analysis are needed. Enigma cribs impose different, rotor-machine constraints.</p>
<figure class="figure" style="margin-top:1rem;">
<svg viewBox="0 0 520 210" style="max-width:560px;width:100%;height:auto;display:block;background:#1a1a2e;border:1px solid #2e2e3a;border-radius:6px;" role="img" aria-label="Hill key recovery: HILL to TCOZ gives an invertible plaintext-block matrix P; K equals C times P inverse modulo 26"><rect x="20" y="40" width="110" height="44" fill="#141422" stroke="#e8d5a3" stroke-width="1.3" rx="5"/><text x="75.0" y="33" style="font-family:ui-monospace,monospace;font-size:9.5px;fill:#8b7355;text-anchor:middle">known plaintext P</text><text x="75.0" y="66.0" style="font-family:ui-monospace,monospace;font-size:12px;fill:#e8d5a3;text-anchor:middle;font-weight:700">HILL</text><rect x="20" y="110" width="110" height="44" fill="#141422" stroke="#C85A5A" stroke-width="1.3" rx="5"/><text x="75.0" y="103" style="font-family:ui-monospace,monospace;font-size:9.5px;fill:#8b7355;text-anchor:middle">its ciphertext C</text><text x="75.0" y="136.0" style="font-family:ui-monospace,monospace;font-size:12px;fill:#C85A5A;text-anchor:middle;font-weight:700">TCOZ</text><path d="M140 62 h40 M140 132 h40 M180 62 v70" fill="none" stroke="#c8b273" stroke-width="1.3"/><line x1="180" y1="97" x2="222" y2="97" stroke="#c8b273" stroke-width="1.3" marker-end="none"/><polygon points="230,97 220,92 220,102" fill="#c8b273"/><rect x="238" y="75" width="120" height="44" fill="#141422" stroke="#d4af37" stroke-width="1.3" rx="5"/><text x="298.0" y="68" style="font-family:ui-monospace,monospace;font-size:9.5px;fill:#8b7355;text-anchor:middle">solve C = K·P</text><text x="298.0" y="101.0" style="font-family:ui-monospace,monospace;font-size:12px;fill:#d4af37;text-anchor:middle;font-weight:700">K = C·P⁻¹</text><line x1="366" y1="97" x2="404" y2="97" stroke="#c8b273" stroke-width="1.3"/><polygon points="412,97 402,92 402,102" fill="#c8b273"/><rect x="420" y="75" width="84" height="44" fill="#141422" stroke="#c8b273" stroke-width="1.3" rx="5"/><text x="462.0" y="68" style="font-family:ui-monospace,monospace;font-size:9.5px;fill:#8b7355;text-anchor:middle">the key</text><text x="462.0" y="101.0" style="font-family:ui-monospace,monospace;font-size:12px;fill:#c8b273;text-anchor:middle;font-weight:700">K</text><text x="262" y="178" style="font-family:ui-monospace,monospace;font-size:10px;fill:#8b7355;text-anchor:middle">two matched blocks: gcd(det(P), 26) must equal 1</text><text x="262" y="194" style="font-family:ui-monospace,monospace;font-size:10px;fill:#8b7355;text-anchor:middle">here det(P) = −11, so K = C·P⁻¹ exists modulo 26</text></svg>
<figcaption class="figure-caption">Hill key recovery — HILL → TCOZ yields K = [[3, 3], [2, 5]] because the plaintext-block matrix is invertible modulo 26.</figcaption>
</figure>
<div class="technique-breaks">
<a href="ciphers/hill.html" class="badge sec-broken" style="text-decoration:none;">Hill Cipher</a>
<a href="ciphers/enigma.html" class="badge sec-broken" style="text-decoration:none;">Enigma</a>
<a href="ciphers/lorenz.html" class="badge sec-broken" style="text-decoration:none;">Lorenz</a>
</div>
</div>
<div class="technique-card" id="hill-climbing">
<div class="technique-num">Technique 06</div>
<div class="technique-name">Hill Climbing Search</div>
<div class="technique-by">Modern · Computer Era</div>
<p class="technique-desc">Start with a random key. Decrypt. Score the result using English language statistics — common digrams like TH, HE, IN. Make random changes to the key. Keep improvements, discard downgrades. Use multiple starting points to explore candidates. Such searches can attack substitution, Playfair and transposition, but may stall at local optima or favor incorrect plaintext.</p>
<figure class="figure" style="margin-top:1rem;">
<svg viewBox="0 0 520 236" style="max-width:560px;width:100%;height:auto;display:block;background:#1a1a2e;border:1px solid #2e2e3a;border-radius:6px;" role="img" aria-label="Hill climbing: greedy steps reach a local peak and stall below the global best"><line x1="20" y1="200" x2="500" y2="200" stroke="#555" stroke-width="1"/><path d="M20 190 C 80 150, 110 92, 150 92 C 190 92, 210 150, 260 165 C 300 176, 330 60, 375 46 C 410 36, 460 90, 500 130" fill="none" stroke="#e8d5a3" stroke-width="1.6"/><circle cx="60" cy="168" r="4" fill="#d4af37" stroke="#141422" stroke-width="1"/><circle cx="90" cy="140" r="4" fill="#d4af37" stroke="#141422" stroke-width="1"/><circle cx="118" cy="110" r="4" fill="#d4af37" stroke="#141422" stroke-width="1"/><circle cx="140" cy="95" r="4" fill="#d4af37" stroke="#141422" stroke-width="1"/><circle cx="150" cy="92" r="6" fill="none" stroke="#C85A5A" stroke-width="1.6"/><text x="150" y="74" style="font-family:ui-monospace,monospace;font-size:10px;fill:#C85A5A;text-anchor:middle">stuck: every single swap scores worse</text><text x="375" y="30" style="font-family:ui-monospace,monospace;font-size:10px;fill:#8b7355;text-anchor:middle">global best — never reached</text><circle cx="375" cy="46" r="5" fill="none" stroke="#555" stroke-width="1.3" stroke-dasharray="3,2"/><text x="260" y="224" style="font-family:ui-monospace,monospace;font-size:10px;fill:#8b7355;text-anchor:middle">key space, one small change per step →</text><text x="30" y="20" style="font-family:ui-monospace,monospace;font-size:10px;fill:#8b7355;text-anchor:start">fitness (English-ness of the decrypt)</text></svg>
<figcaption class="figure-caption">Hill climbing search — iteratively refining key guesses by scoring decrypted output against English language statistics.</figcaption>
</figure>
<div class="technique-breaks">
<a href="ciphers/monoalphabetic.html" class="badge sec-broken" style="text-decoration:none;">Substitution</a>
<a href="ciphers/playfair.html" class="badge sec-broken" style="text-decoration:none;">Playfair</a>
<span class="badge sec-broken">Transposition</span>
</div>
</div>
<div class="technique-card">
<div class="technique-num" id="simulated-annealing">Technique 07</div>
<div class="technique-name">Simulated Annealing / Genetic Algorithms</div>
<div class="technique-by">Modern · AI-Assisted</div>
<p class="technique-desc">Genetic algorithms explore populations of candidate keys; simulated annealing sometimes accepts worse-scoring moves. These methods can help attack classical ciphers, but time and success depend on the problem and settings. A high language score does not prove a correct plaintext.</p>
<figure class="figure" style="margin-top:1rem;">
<svg viewBox="0 0 520 236" style="max-width:560px;width:100%;height:auto;display:block;background:#1a1a2e;border:1px solid #2e2e3a;border-radius:6px;" role="img" aria-label="Simulated annealing: an accepted downhill jump escapes the local peak and reaches the global best"><line x1="20" y1="200" x2="500" y2="200" stroke="#555" stroke-width="1"/><path d="M20 190 C 80 150, 110 92, 150 92 C 190 92, 210 150, 260 165 C 300 176, 330 60, 375 46 C 410 36, 460 90, 500 130" fill="none" stroke="#e8d5a3" stroke-width="1.6"/><circle cx="60" cy="168" r="4" fill="#c8b273" stroke="#141422" stroke-width="1"/><circle cx="90" cy="140" r="4" fill="#c8b273" stroke="#141422" stroke-width="1"/><circle cx="118" cy="110" r="4" fill="#c8b273" stroke="#141422" stroke-width="1"/><circle cx="140" cy="95" r="4" fill="#c8b273" stroke="#141422" stroke-width="1"/><path d="M150 88 C 200 60, 230 190, 268 168" fill="none" stroke="#C85A5A" stroke-width="1.4" stroke-dasharray="5,3"/><polygon points="272,166 262,166 268,176" fill="#C85A5A"/><text x="212" y="206" style="font-family:ui-monospace,monospace;font-size:10px;fill:#C85A5A;text-anchor:middle">worse move accepted while "hot"</text><circle cx="300" cy="140" r="4" fill="#d4af37" stroke="#141422" stroke-width="1"/><circle cx="335" cy="80" r="4" fill="#d4af37" stroke="#141422" stroke-width="1"/><circle cx="360" cy="55" r="4" fill="#d4af37" stroke="#141422" stroke-width="1"/><circle cx="375" cy="46" r="4" fill="#d4af37" stroke="#141422" stroke-width="1"/><circle cx="375" cy="46" r="7" fill="none" stroke="#d4af37" stroke-width="1.8"/><text x="375" y="28" style="font-family:ui-monospace,monospace;font-size:11px;fill:#d4af37;text-anchor:middle;font-weight:700">global best ✓</text><text x="260" y="224" style="font-family:ui-monospace,monospace;font-size:10px;fill:#8b7355;text-anchor:middle">temperature cools → downhill jumps become rare</text><text x="30" y="20" style="font-family:ui-monospace,monospace;font-size:10px;fill:#8b7355;text-anchor:start">fitness</text></svg>
<figcaption class="figure-caption">Simulated annealing — one possible escape from a local optimum; a finite run need not reach the global best.</figcaption>
</figure>
<div class="technique-breaks">
<a href="ciphers/double-transposition.html" class="badge sec-broken" style="text-decoration:none;">Double Transposition</a>
<a href="ciphers/playfair.html" class="badge sec-broken" style="text-decoration:none;">Playfair</a>
<a href="ciphers/hill.html" class="badge sec-broken" style="text-decoration:none;">Hill Cipher</a>
</div>
</div>
<div class="technique-card">
<div class="technique-num">Technique 08</div>
<div class="technique-name">Stepping-Switch Cryptanalysis</div>
<div class="technique-by">Frank Rowlett & Genevieve Grotjan · SIS · 1939–1940</div>
<p class="technique-desc">Japan's Purple machine routed plaintext through banks of telephone-style stepping switches rather than rotors. The US Signals Intelligence Service had no machine to study, so they hunted statistical regularities in intercept traffic — looking for cycles in how the switches advanced. Genevieve Grotjan’s September 1940 insight was a crucial step in the team’s reconstruction of the switch behavior. SIS built an analog machine, enabling decryption of Purple diplomatic messages. MAGIC was the wider intelligence product, not the name of this single attack.</p>
<div class="technique-breaks">
<a href="ciphers/purple.html" class="badge sec-broken" style="text-decoration:none;">Purple</a>
<span class="badge sec-broken">Stepping-switch machines</span>
</div>
</div>
<div class="technique-card">
<div class="technique-num">Technique 09</div>
<div class="technique-name">Computer-Assisted Manuscript Decipherment</div>
<div class="technique-by">Knight, Megyesi & Schaefer · USC ISI / Uppsala · 2011</div>
<p class="technique-desc">Knight, Megyesi and Schaefer combined careful transcription, symbol statistics and linguistic reasoning to decipher Copiale. Their 2011 paper describes unsuccessful early automatic-decryption attempts, then progress after treating the Roman letters as spaces and grouping likely homophones. This was a combination of computation and human analysis, not an EM algorithm automatically discovering the answer. <a href="https://aclanthology.org/W11-1202/" target="_blank" rel="noopener noreferrer">Original Copiale paper</a>.</p>
<div class="technique-breaks">
<a href="ciphers/copiale.html" class="badge sec-broken" style="text-decoration:none;">Copiale</a>
<a href="ciphers/homophonic.html" class="badge sec-broken" style="text-decoration:none;">Homophonic</a>
</div>
</div>
<div class="technique-card">
<div class="technique-num">Technique 10</div>
<div class="technique-name">Chaocipher Reconstruction</div>
<div class="technique-by">Moshe Rubin & George Lasry · 2010–2014</div>
<p class="technique-desc">The disclosure of Byrne’s Chaocipher algorithm in 2010 made independent implementation and attack possible. Subsequent research studied known-plaintext and ciphertext-only attacks under specified conditions. Distinguish revealing an algorithm from recovering a secret starting state. <a href="https://www.chaocipher.com/chaocipher-current-state-of-research.htm" target="_blank" rel="noopener noreferrer">Researchers’ record of attacks and solved exhibits</a>.</p>
<div class="technique-breaks">
<a href="ciphers/chaocipher.html" class="badge sec-broken" style="text-decoration:none;">Chaocipher</a>
</div>
</div>
</div>
<div class="callout callout-warn" style="margin-bottom:2rem;">
<span class="callout-icon">⚡</span>
<div class="callout-body">
<p><strong>Search speed depends on the problem:</strong> Short repeating keys and long, ordinary-language messages can be easy for automated solvers. Short texts, unusual vocabulary, layered ciphers and poorly chosen scoring models can remain difficult. The demonstrations here are examples, not measured performance guarantees.</p>
</div>
</div>
<svg class="section-divider" viewBox="0 0 180 6"><line x1="0" y1="3" x2="180" y2="3"/></svg>
<!-- Interactive Lab -->
<div class="section-head">
<span class="section-eyebrow">Hands-On</span>
<h2 class="section-title">Try the Techniques</h2>
<p class="section-sub">Apply cryptanalysis tools to real ciphertext.</p>
</div>
<div class="lab-tabs" role="tablist" aria-label="Cryptanalysis techniques">
<button class="lab-tab active" id="tab-btn-freq" role="tab" aria-selected="true" aria-controls="tab-freq" tabindex="0" data-fn="showTab" data-arg="freq">Frequency Analysis</button>
<button class="lab-tab" id="tab-btn-kasiski" role="tab" aria-selected="false" aria-controls="tab-kasiski" tabindex="-1" data-fn="showTab" data-arg="kasiski">Kasiski Examination</button>
<button class="lab-tab" id="tab-btn-ioc" role="tab" aria-selected="false" aria-controls="tab-ioc" tabindex="-1" data-fn="showTab" data-arg="ioc">Index of Coincidence</button>
<button class="lab-tab" id="tab-btn-crib" role="tab" aria-selected="false" aria-controls="tab-crib" tabindex="-1" data-fn="showTab" data-arg="crib">Crib Dragging</button>
<button class="lab-tab" id="tab-btn-kpa" role="tab" aria-selected="false" aria-controls="tab-kpa" tabindex="-1" data-fn="showTab" data-arg="kpa">Known Plaintext</button>
<button class="lab-tab" id="tab-btn-hillclimb" role="tab" aria-selected="false" aria-controls="tab-hillclimb" tabindex="-1" data-fn="showTab" data-arg="hillclimb">Hill Climbing</button>
<button class="lab-tab" id="tab-btn-anneal" role="tab" aria-selected="false" aria-controls="tab-anneal" tabindex="-1" data-fn="showTab" data-arg="anneal">Simulated Annealing</button>
</div>
<!-- Tab: Frequency Analysis (SVG bar chart with English reference) -->
<div class="lab-panel active" id="tab-freq" role="tabpanel" aria-labelledby="tab-btn-freq">
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:2rem;">
<div>
<div class="input-group" style="margin-bottom:1rem;">
<label class="input-label" for="labFreqInput">Paste Ciphertext</label>
<textarea class="museum-input" id="labFreqInput" rows="5" placeholder="Paste any ciphertext here… Try: KHOOR ZRUOG (Caesar shift 3)" oninput="labFreqUpdate()"></textarea>
</div>
<div id="labFreqResult" aria-live="polite" style="font-family:var(--fm);font-size:.82rem;color:var(--tx2);line-height:1.8;"></div>
<p id="labFreqReading" aria-live="polite" style="margin-top:1rem;font-family:var(--fb);font-size:.95rem;color:var(--tx2);line-height:1.7;"></p>
<p style="margin-top:1rem;font-family:var(--fm);font-size:.72rem;color:var(--tx3);line-height:1.6;">IC = Σ n<sub>i</sub>(n<sub>i</sub>−1) / N(N−1) · <span id="labFreqIC" style="color:var(--gold);">—</span></p>
</div>
<div>
<p class="input-label" style="margin-bottom:.5rem;">Letter Frequencies <span style="font-size:.7rem;color:var(--tx3);">(gold = input, outline = English)</span></p>
<svg id="labFreqSVG" viewBox="0 0 572 170" style="width:100%;height:auto;" role="img" aria-label="Letter frequency bar chart"></svg>
<div id="labFreqHint" aria-live="polite" style="font-family:var(--fm);font-size:.72rem;color:var(--gold);margin-top:.75rem;"></div>
</div>
</div>
<div class="callout callout-info" style="margin-top:2rem;">
<span class="callout-icon">📜</span>
<div class="callout-body">
<p>Al-Kindi’s surviving treatise describes frequency-based cryptanalysis. A monoalphabetic substitution preserves counts while relabeling symbols; polyalphabetic substitution changes that relationship, but need not make a short sample uniform. Language frequencies are evidence for hypotheses, not a guarantee of successful decryption.</p>
</div>
</div>
</div>
<!-- Tab: Kasiski -->
<div class="lab-panel" id="tab-kasiski" role="tabpanel" aria-labelledby="tab-btn-kasiski" hidden>
<div style="max-width:700px;">
<div class="input-group" style="margin-bottom:1.25rem;">
<label class="input-label" for="kasiskiInput">Paste Vigenère Ciphertext (strip spaces)</label>
<textarea class="museum-input" id="kasiskiInput" rows="4" placeholder="LXFOPVEFRNHRLXFOPVEFRNHR… Repeated sequences suggest candidate periods." oninput="runKasiski()"></textarea>
</div>
<div id="kasiskiResult" aria-live="polite" style="font-family:var(--fm);font-size:.82rem;color:var(--tx2);line-height:1.8;"></div>
<div class="kasiski-hits" id="kasiskiHits" aria-live="polite"></div>
</div>
</div>
<!-- Tab: Index of Coincidence -->
<div class="lab-panel" id="tab-ioc" role="tabpanel" aria-labelledby="tab-btn-ioc" hidden>
<div style="max-width:600px;">
<div class="input-group" style="margin-bottom:1.25rem;">
<span class="input-label">Paste Ciphertext</span>
<textarea class="museum-input" id="iocInput" aria-label="Ciphertext input for index of coincidence" rows="4" placeholder="Paste ciphertext to calculate IC…" oninput="calcIC()"></textarea>
</div>
<div class="ic-display" id="icValue">—</div>
<div class="ic-label">Index of Coincidence</div>
<div class="ic-bar-wrap" style="margin-top:1rem;">
<div class="ic-bar" id="icBar" style="width:0%"></div>
</div>
<div style="display:flex;justify-content:space-between;font-family:var(--fm);font-size:.6rem;color:var(--tx3);">
<span>0.0385 (uniform reference)</span>
<span>Reference comparison scale</span>
<span>~0.066 (English reference)</span>
</div>
<div id="icInterpret" aria-live="polite" style="margin-top:1.5rem;font-family:var(--fm);font-size:.8rem;color:var(--tx2);line-height:1.7;"></div>
</div>
</div>
<!-- Tab: Crib Dragging -->
<div class="lab-panel" id="tab-crib" role="tabpanel" aria-labelledby="tab-btn-crib" hidden>
<div style="max-width:700px;">
<p style="margin-bottom:1rem;color:var(--tx2);"><strong>Additive-cipher crib test.</strong> Slide a guessed plaintext word across a Vigenère or running-key ciphertext. At each position this tool computes a possible key fragment, (ciphertext − crib) mod 26, with A = 0. A repeating fragment is a hypothesis to test against other text, not confirmation of the crib or key. <a href="ciphers/enigma.html">Enigma crib attacks</a> instead test rotor and plugboard constraints; this subtraction is not the Enigma attack.</p>
<div class="input-group" style="margin-bottom:1rem;">
<label class="input-label" for="cribCipher">Ciphertext (letters only)</label>
<textarea class="museum-input" id="cribCipher" rows="3" placeholder="LXFOPVEFRNHRLXFOPVEFRNHR… Try a Vigenère ciphertext."></textarea>
</div>
<div class="input-group" style="margin-bottom:1rem;">
<label class="input-label" for="cribWord">Crib (guessed plaintext word)</label>
<input type="text" class="museum-input" id="cribWord" placeholder="e.g. ATTACK or WEATHER" autocomplete="off" spellcheck="false" style="font-family:var(--fm);letter-spacing:.1em;">
</div>
<button class="btn btn-primary" data-fn="runCribDrag" style="font-size:.7rem;">Drag Crib</button>
<div id="cribResult" class="lab-result" aria-live="polite"></div>
</div>
</div>
<!-- Tab: Known Plaintext Attack -->
<div class="lab-panel" id="tab-kpa" role="tabpanel" aria-labelledby="tab-btn-kpa" hidden>
<div style="max-width:700px;">
<p style="margin-bottom:1rem;color:var(--tx2);">For an additive cipher such as Vigenère, known plaintext reveals the keystream <em>only at the matching positions</em>: Key[i] = (Cipher[i] − Plain[i]) mod 26. Align both inputs at their first letter. A repeating fragment suggests a possible period; finite observations do not prove that the rest of the key repeats.</p>
<div class="input-group" style="margin-bottom:1rem;">
<label class="input-label" for="kpaPlain">Known plaintext prefix</label>
<input type="text" class="museum-input" id="kpaPlain" placeholder="e.g. ATTACKATDAWN" autocomplete="off" spellcheck="false" style="font-family:var(--fm);letter-spacing:.1em;">
</div>
<div class="input-group" style="margin-bottom:1rem;">
<label class="input-label" for="kpaCipher">Corresponding Ciphertext</label>
<input type="text" class="museum-input" id="kpaCipher" placeholder="e.g. LXFOPVEFRNHR" autocomplete="off" spellcheck="false" style="font-family:var(--fm);letter-spacing:.1em;">
</div>
<div class="input-group" style="margin-bottom:1rem;">
<label class="input-label" for="kpaPeriod">Assumed repeating-key period (optional)</label>
<input type="number" class="museum-input" id="kpaPeriod" min="1" step="1" placeholder="Leave blank to show only supported positions" aria-describedby="kpaPeriodHelp">
<p id="kpaPeriodHelp" style="margin-top:.5rem;color:var(--tx2);">To explore a hypothesis, enter a period no longer than the observed keystream. Any extension beyond the known plaintext remains unverified.</p>
</div>
<button class="btn btn-primary" data-fn="runKPA" style="font-size:.7rem;">Inspect Keystream</button>
<div id="kpaResult" class="lab-result" aria-live="polite"></div>
<p style="margin-top:1rem;color:var(--tx2);">Reading: Joshua Holden, <a href="further-reading.html#holden"><em>The Mathematics of Secrets: Cryptography from Caesar Ciphers to Digital Encryption</em></a> (2017 edition), §§5.1–5.2, distinguishes repeating keys, running keys, and one-time pads.</p>
</div>
</div>
<!-- Tab: Hill Climbing -->
<div class="lab-panel" id="tab-hillclimb" role="tabpanel" aria-labelledby="tab-btn-hillclimb" hidden>
<div style="max-width:700px;">
<p style="margin-bottom:1rem;color:var(--tx2);">Watch a small Caesar-key search score candidate shifts against English frequencies. It illustrates greedy search, not a guaranteed solve: a short or unusual message may score best under an incorrect key.</p>
<div class="input-group" style="margin-bottom:1rem;">
<label class="input-label" for="hillInput">Ciphertext</label>
<input type="text" class="museum-input" id="hillInput" value="KHOOR ZRUOG" autocomplete="off" spellcheck="false" style="font-family:var(--fm);letter-spacing:.1em;">
</div>
<button class="btn btn-primary" id="hillBtn" data-fn="runHillClimb" style="font-size:.7rem;">Run Hill Climb</button>
<div id="hillOutput" style="margin-top:1.25rem;font-family:var(--fm);font-size:.82rem;line-height:2.2;color:var(--tx2);"></div>
<p>The objective is the mean reference frequency assigned to each letter: <code>sum(reference frequency of each letter) / letter count</code>. Higher scores are preferred. This measures a preference for common letters, not distance from an English frequency distribution.</p>
<p>Counterexample: <code>HELLOWORLD</code> and its reversal <code>DLROWOLLEH</code> both score 5.85; <code>EEEEEEEEEE</code> scores 12.7. A score optimum can be wrong even when every key has been checked.</p>
<button class="btn" id="hillExample" type="button">Restore supplied known-answer example</button>
<div id="hillLandscape" style="margin-top:1rem;overflow-wrap:anywhere;" aria-live="polite"></div>
</div>
</div>
<!-- Tab: Simulated Annealing -->
<div class="lab-panel" id="tab-anneal" role="tabpanel" aria-labelledby="tab-btn-anneal" hidden>
<div style="max-width:700px;">
<p style="margin-bottom:1rem;color:var(--tx2);">Simulated annealing sometimes accepts worse-scoring candidates to escape local optima. This Caesar example illustrates the rule; a finite run does not guarantee the globally best score or the authentic plaintext.</p>
<div class="input-group" style="margin-bottom:1rem;">
<label class="input-label" for="annealInput">Ciphertext (Caesar)</label>
<input type="text" class="museum-input" id="annealInput" value="WKLV LV D WHVW PHVVDJH" autocomplete="off" spellcheck="false" style="font-family:var(--fm);letter-spacing:.1em;">
</div>
<button class="btn btn-primary" id="annealBtn" data-fn="runAnneal" style="font-size:.7rem;">Run Annealing</button>
<div id="annealOutput" style="margin-top:1.25rem;font-family:var(--fm);font-size:.82rem;line-height:2;color:var(--tx2);"></div>
<p>The objective is the mean reference frequency assigned to each letter: <code>sum(reference frequency of each letter) / letter count</code>. Higher scores are preferred. This measures a preference for common letters, not distance from an English frequency distribution.</p>
<p>Counterexample: <code>HELLOWORLD</code> and its reversal <code>DLROWOLLEH</code> both score 5.85; <code>EEEEEEEEEE</code> scores 12.7. A score optimum can be wrong even when every key has been checked.</p>
<button class="btn" id="annealExample" type="button">Restore supplied known-answer example</button>
<div id="annealLandscape" style="margin-top:1rem;overflow-wrap:anywhere;" aria-live="polite"></div>
</div>
</div>
<!-- 12 Famous Codebreaks -->
<div class="reveal" style="margin-top:5rem;">
<div class="section-head">
<span class="section-eyebrow">Historical Record</span>
<h2 class="section-title">12 Famous Codebreaks in History</h2>
<p class="section-sub">Selected attacks and advances, with their evidence and limitations.</p>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:0;" id="timeline">
<div class="timeline" style="padding-right:2rem;">
<div class="tl-item legendary">
<div class="tl-year">850 AD</div>
<div class="tl-body">
<div class="tl-title">Al-Kindi Breaks Substitution</div>
<div class="tl-person">Al-Kindi · Baghdad</div>
<div class="tl-technique">Technique: Frequency Analysis</div>
<p class="tl-impact">An early surviving systematic account of frequency analysis. Its effectiveness depends on the cipher and available text.</p>
</div>
</div>
<div class="tl-item">
<div class="tl-year">1850s</div>
<div class="tl-body">
<div class="tl-title">Babbage Breaks Vigenère</div>
<div class="tl-person">Charles Babbage</div>
<div class="tl-technique">Technique: Repeating Sequence Analysis</div>
<p class="tl-impact">Babbage worked on attacks against repeating-key polyalphabetic ciphers but did not publish his method. Kasiski independently published an approach in 1863.</p>
</div>
</div>
<div class="tl-item">
<div class="tl-year">1863</div>
<div class="tl-body">
<div class="tl-title">Kasiski Publishes the Method</div>
<div class="tl-person">Friedrich Kasiski</div>
<div class="tl-technique">Technique: Pattern Repetition Analysis</div>
<p class="tl-impact">Kasiski published a method using repeated ciphertext groups to investigate the period of repeating-key polyalphabetic ciphers. Publication did not make every polyalphabetic system immediately readable.</p>
</div>
</div>
<div class="tl-item legendary">
<div class="tl-year">1932</div>
<div class="tl-body">
<div class="tl-title">Polish Mathematicians Break Enigma</div>
<div class="tl-person">Rejewski, Różycki, Zygalski · Warsaw</div>
<div class="tl-technique">Technique: Permutation Analysis · Known Plaintext</div>
<p class="tl-impact">Rejewski reconstructed military Enigma in 1932; Polish analysts subsequently developed several attacks and aids, including the bomba in 1938. Their July 1939 exchange with Britain and France shared methods and reconstructed machines.</p>
</div>
</div>
<div class="tl-item">
<div class="tl-year">1940</div>
<div class="tl-body">
<div class="tl-title">US SIS Breaks Japanese Purple</div>
<div class="tl-person">Friedman's SIS team · Grotjan's insight · Washington DC</div>
<div class="tl-technique">Technique: Statistical Analysis · Machine Reconstruction</div>
<p class="tl-impact">Purple provided valuable Japanese diplomatic intelligence. It did not reveal a complete operational plan for the Pearl Harbor attack; diplomatic and naval communications used different systems.</p>
</div>
</div>
<div class="tl-item">
<div class="tl-year">1974 · 1990</div>
<div class="tl-body">
<div class="tl-title">DES and Differential Cryptanalysis</div>
<div class="tl-person">IBM & NSA · rediscovered by Eli Biham & Adi Shamir</div>
<div class="tl-technique">Technique: Differential Cryptanalysis</div>
<p class="tl-impact">IBM’s designers knew a form of differential cryptanalysis while developing DES and used it in selecting the S-boxes. Biham and Shamir later developed the technique publicly. Its effectiveness depends on the cipher and the available data.</p>
</div>
</div>
</div>
<div class="timeline" style="padding-left:2rem;border-left:1px solid var(--s4);">
<div class="tl-item legendary">
<div class="tl-year">WWII</div>
<div class="tl-body">
<div class="tl-title">Bletchley Park Breaks Enigma</div>
<div class="tl-person">Alan Turing · Gordon Welchman</div>
<div class="tl-technique">Technique: Crib Attacks · Electromechanical Bombe</div>
<p class="tl-impact">Polish mathematicians reconstructed military Enigma before the war. Later Allied bombe machines used crib-derived constraints to eliminate settings. The intelligence contribution was substantial; a precise number of years removed from the war cannot be measured.</p>
</div>
</div>
<div class="tl-item legendary">
<div class="tl-year">1941–1944</div>
<div class="tl-body">
<div class="tl-title">From the Lorenz Depth to Colossus</div>
<div class="tl-person">Bill Tutte · Tommy Flowers</div>
<div class="tl-technique">Technique: Known Plaintext · Early Computer Search</div>
<p class="tl-impact">Tiltman’s 1941 work and Tutte’s structural reconstruction preceded Colossus. The electronic machine became operational in February 1944 and accelerated statistical wheel-setting searches.</p>
</div>
</div>
<div class="tl-item">
<div class="tl-year">1943–80</div>
<div class="tl-body">
<div class="tl-title">VENONA: Reused Pad Material</div>
<div class="tl-person">US Army Signal Intelligence</div>
<div class="tl-technique">Technique: Key Reuse Exploitation</div>
<p class="tl-impact">Duplicated Soviet pad material enabled attacks on some traffic. Analysts also had to reconstruct codebook meanings; recovery was partial and often took years. VENONA’s success did not break the perfect-secrecy guarantee of a correctly used one-time pad.</p>
</div>
</div>
<div class="tl-item">
<div class="tl-year">1993</div>
<div class="tl-body">
<div class="tl-title">Linear Cryptanalysis vs DES</div>
<div class="tl-person">Mitsuru Matsui</div>
<div class="tl-technique">Technique: Linear Cryptanalysis</div>
<p class="tl-impact">Linear approximations enabled a statistical attack on DES under a large known-plaintext requirement. The often-cited 2⁴³ figure is a data requirement for a particular attack, not an unconditional reduction of all attack costs to 2⁴³.</p>
</div>
</div>
<div class="tl-item">
<div class="tl-year">1996</div>
<div class="tl-body">
<div class="tl-title">RSA Timing Attack</div>
<div class="tl-person">Paul Kocher</div>
<div class="tl-technique">Technique: Side-Channel Timing Analysis</div>
<p class="tl-impact">Kocher described how data-dependent execution times can leak secret information from implementations, including RSA. Such attacks require suitable observations and vulnerable implementations; they do not solve the underlying factoring problem.</p>
</div>
</div>
<div class="tl-item">
<div class="tl-year">2017</div>
<div class="tl-body">
<div class="tl-title">SHA-1 Collision (SHAttered)</div>
<div class="tl-person">Google · CWI Institute</div>
<div class="tl-technique">Technique: Collision Attack</div>
<p class="tl-impact">The SHAttered team produced two different PDFs with the same SHA-1 digest. This demonstrated a practical collision and reinforced a migration already underway; collision resistance and preimage resistance are different properties.</p>
</div>
</div>
</div>
</div>
</div>
<!-- Big Pattern -->
<div style="margin-top:4rem;">
<div class="callout callout-mystery">
<span class="callout-icon">🔍</span>
<div class="callout-body">
<p><strong>The Big Pattern:</strong> These cases combine mathematical analysis with <strong>operational evidence</strong>, <strong>protocol weaknesses</strong> and <strong>implementation behavior</strong>. Reused pad material, predictable plaintext and timing leakage create different attack opportunities. Predictable plaintext is not itself an error: modern encryption should protect it under the stated attack model.</p>
</div>
</div>
</div>
</div><!-- /section -->
<section class="section"><h2>From papers to inspectable evidence</h2><p><a href="lab/evidence-workbench.html">Evidence Workbench</a> · <a href="lab/decipherment-cases.html">Copiale, Mary Stuart and Z340 investigations</a> · <a href="lab/modern-attack-evidence.html">Modern attack conditions</a></p></section>
<section class="section"><h2>Reading the search score</h2><p>George Lasry, <cite>A Methodology for the Cryptanalysis of Classical Ciphers with Search Metaheuristics</cite> (2018 published thesis), §§3.2.1–3.2.4, printed pp. 29–34 / PDF pp. 50–55, and §4.5, printed pp. 56–57 / PDF pp. 77–78, distinguishes objectives, selectivity and resilience to key errors. The small Caesar landscape above is an original teaching example; it does not reproduce the thesis’s large-key experiments.</p></section>
<section class="section"><h2>Reading</h2><p>Joshua Holden, <em>The Mathematics of Secrets: Cryptography from Caesar Ciphers to Digital Encryption</em> (2017 edition), chapter 2 and §§5.1–5.2; David Kahn, <em>The Codebreakers: The Comprehensive History of Secret Communication from Ancient Times to the Internet</em> (revised edition, 1996); Robert L. Benson, <em>The VENONA Story</em> (Center for Cryptologic History edition, 2001), pp. 1–3, 7 and 14–17. The demonstrations illustrate hypotheses and search rules, not guaranteed recoveries.</p></section>
</main>
<footer></footer>
<script>
function escapeLabText(text) {
return text.replace(/[&<>"']/g, ch => ({"&":"&", "<":"<", ">":">", '"':""", "'":"'"}[ch]));
}
function showTab(id, button) {
const targetPanel = document.getElementById('tab-' + id);
const targetButton = button || document.getElementById('tab-btn-' + id);
document.querySelectorAll('.lab-panel').forEach(panel => {
const isActive = panel === targetPanel;
panel.classList.toggle('active', isActive);
panel.hidden = !isActive;
});
document.querySelectorAll('[id^="tab-btn-"]').forEach(tab => {
const isActive = tab === targetButton;
tab.classList.toggle('active', isActive);
tab.setAttribute('aria-selected', isActive ? 'true' : 'false');
tab.tabIndex = isActive ? 0 : -1;
});
if (targetButton) {
targetButton.focus();
}
}
document.querySelectorAll('.lab-tab').forEach((tab, index, tabs) => {
tab.addEventListener('keydown', event => {
const previous = (index - 1 + tabs.length) % tabs.length;
const next = (index + 1) % tabs.length;
if (event.key === 'ArrowLeft') {
event.preventDefault();
tabs[previous].click();
} else if (event.key === 'ArrowRight') {
event.preventDefault();
tabs[next].click();
} else if (event.key === 'Home') {
event.preventDefault();
tabs[0].click();
} else if (event.key === 'End') {
event.preventDefault();
tabs[tabs.length - 1].click();
}
});
});
// ── Frequency Analysis ──────────────────────────────
const ALPHA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
const ENG_FREQ = [8.2,1.5,2.8,4.3,12.7,2.2,2.0,6.1,7.0,0.15,0.77,4.0,2.4,6.7,7.5,1.9,0.095,6.0,6.3,9.1,2.8,0.98,2.4,0.15,2.0,0.074];
function buildLabFreqSVG() {
const svg = document.getElementById('labFreqSVG');
if (!svg) return;
const bw = 16, gap = 6, h = 140, base = 155, maxEng = 12.7;
let html = '';
for (let i = 0; i < 26; i++) {
const x = i * (bw + gap);
const eh = (ENG_FREQ[i] / maxEng) * h;
// English reference bar (outline)
html += '<rect x="' + x + '" y="' + (base - eh) + '" width="' + bw + '" height="' + eh + '" fill="none" stroke="rgba(201,168,76,0.3)" stroke-width="1" rx="1"/>';
// Input frequency bar (filled, initially zero height)
html += '<rect id="svgbar_' + ALPHA[i] + '" x="' + x + '" y="' + base + '" width="' + bw + '" height="0" fill="#C9A84C" rx="1" style="transition:y .3s,height .3s;"/>';
// Letter label
html += '<text x="' + (x + bw / 2) + '" y="' + (base + 12) + '" text-anchor="middle" fill="#706878" font-family="monospace" font-size="8">' + ALPHA[i] + '</text>';
}
svg.innerHTML = html;
}
buildLabFreqSVG();
function labFreqUpdate() {
const txt = document.getElementById('labFreqInput').value.toUpperCase().replace(/[^A-Z]/g,'');
const result = document.getElementById('labFreqResult');
const hint = document.getElementById('labFreqHint');
const icEl = document.getElementById('labFreqIC');
const reading = document.getElementById('labFreqReading');
if (!txt.length) { hint.textContent=''; result.textContent=''; icEl.textContent='—'; if(reading)reading.textContent=''; resetSVGBars(); return; }
const counts={}, len=txt.length, maxEng=12.7, h=140, base=155;
for (const ch of txt) counts[ch]=(counts[ch]||0)+1;
let maxCh='E',maxPct=0;
for (let i=0;i<26;i++){
const ch=ALPHA[i], pct=((counts[ch]||0)/len)*100;
const barH=(pct/maxEng)*h;
const el=document.getElementById('svgbar_'+ch);
if(el){el.setAttribute('y',base-barH);el.setAttribute('height',barH);el.setAttribute('fill',pct>8?'#C9A84C':pct>4?'#9A7A40':'#7a6530');}
if(pct>maxPct){maxPct=pct;maxCh=ch;}
}
// Compute IC inline
let icSum=0;
for (const k in counts) icSum+=counts[k]*(counts[k]-1);
const ic=len>1?icSum/(len*(len-1)):0;
icEl.textContent=len>1?ic.toFixed(4):'— (needs two letters)';
const guessShift=(maxCh.charCodeAt(0)-69+26)%26;
hint.textContent=`Most frequent: ${maxCh} (${maxPct.toFixed(1)}%) → If this is E, shift = ${guessShift}`;
result.innerHTML=`<strong>Total letters:</strong> ${len}<br><strong>Most common:</strong> ${maxCh} = ${maxPct.toFixed(1)}%<br><strong>Caesar hypothesis if the most frequent symbol represents E:</strong> ${guessShift}`;
if (reading) {
reading.innerHTML = `Your text's most common letter is <strong style="color:var(--gold-lt)">${maxCh}</strong> (${maxPct.toFixed(1)}%). In many long English samples, the most common letter is <strong style="color:var(--gold-lt)">E</strong> (12.7%). If this is a monoalphabetic substitution, <strong>${maxCh}</strong> is a candidate for <strong>E</strong> in the plaintext; short or unusual samples may differ.`;
}
}
function resetSVGBars(){for(let i=0;i<26;i++){const el=document.getElementById('svgbar_'+ALPHA[i]);if(el){el.setAttribute('y',155);el.setAttribute('height',0);}}}
// ── Index of Coincidence ────────────────────────────
function calcIC() {
const txt = document.getElementById('iocInput').value.toUpperCase().replace(/[^A-Z]/g,'');
const display = document.getElementById('icValue');
const bar = document.getElementById('icBar');
const interp = document.getElementById('icInterpret');
if (txt.length < 2) { display.textContent='—'; bar.style.width='0%'; interp.textContent=''; return; }
const counts={};
for (const ch of txt) counts[ch]=(counts[ch]||0)+1;
const n=txt.length;
let sum=0;
for (const k in counts) sum+=counts[k]*(counts[k]-1);
const ic=(sum/(n*(n-1)));
display.textContent=ic.toFixed(4);
const pct=Math.min(100,Math.max(0,((ic-0.0385)/(0.066-0.0385))*100));
bar.style.width=pct+'%';
let msg='';
if(ic > 0.080) msg='The observed IC is above the usual long-English reference. Repetitive or short text can produce high values; this does not identify a cipher.';
else if(ic >= 0.060) msg='This IC is near the long-English reference. Plaintext, transposition and fixed substitution can all preserve this structure; other constructions can also produce it.';
else if(ic >= 0.045) msg='This IC lies between common English and uniform-letter reference values. A repeating-key cipher is one hypothesis, not a diagnosis.';
else msg='This IC is near or below the uniform-letter reference. Short samples can even have IC 0; the value does not prove randomness or a long key.';
msg += ' Observed IC ranges from 0 to 1. The bar clips values outside its reference comparison scale. Consider message length, language and other evidence.';
interp.innerHTML=msg;
}
// ── Kasiski Examination ─────────────────────────────
function runKasiski() {
const txt = document.getElementById('kasiskiInput').value.toUpperCase().replace(/[^A-Z]/g,'');
const result = document.getElementById('kasiskiResult');
const hits = document.getElementById('kasiskiHits');
if (txt.length < 20) { result.textContent='Enter at least 20 characters.'; hits.innerHTML=''; return; }
const repeats={};
for (let len=3;len<=6;len++) {
for (let i=0;i<=txt.length-len;i++) {
const seq=txt.substr(i,len);
if (!repeats[seq]) repeats[seq]=[];
repeats[seq].push(i);
}
}
const found=[];
for (const seq in repeats) {
if (repeats[seq].length>1) {
const positions=repeats[seq];
const spacings=[];
for (let i=1;i<positions.length;i++) spacings.push(positions[i]-positions[i-1]);
found.push({seq,positions,spacings});
}
}
if (!found.length) { result.textContent='No repeated sequences found. Text may be too short, or cipher is not Vigenère with a short key.'; hits.innerHTML=''; return; }
// GCD analysis
const allSpacings=found.flatMap(f=>f.spacings);
const gcds={};
for (const s of allSpacings) {
for (let k=2;k<=Math.min(s,15);k++) {
if (s%k===0) gcds[k]=(gcds[k]||0)+1;
}
}
const sorted=Object.entries(gcds).sort((a,b)=>b[1]-a[1]);
const likelyKey=sorted[0]?sorted[0][0]:null;
result.innerHTML=`Found <strong>${found.length}</strong> repeated sequences. Top spacing-factor candidate (2–15): <strong style="color:var(--gold)">${likelyKey || "none"}</strong>. This heuristic counts overlapping repeats; accidental repeats and tied factors require further tests.`;
hits.innerHTML=found.slice(0,8).map(f=>
`<div class="kasiski-hit">"${f.seq}" at positions ${f.positions.join(', ')} — spacings: ${f.spacings.join(', ')}</div>`
).join('');
}
// ── Crib Dragging ───────────────────────────────────
// Require at least two complete periods. A match describes only this fragment;
// it does not establish either the guessed plaintext or the complete key.
function fragmentPeriod(fragment) {
for (let period = 1; period <= Math.floor(fragment.length / 2); period++) {
let fits = true;
for (let i = period; i < fragment.length; i++) {
if (fragment[i] !== fragment[i % period]) { fits = false; break; }
}
if (fits) return period;
}
return null;
}
function runCribDrag() {
const ct = document.getElementById('cribCipher').value.toUpperCase().replace(/[^A-Z]/g,'');
const crib = document.getElementById('cribWord').value.toUpperCase().replace(/[^A-Z]/g,'');
const out = document.getElementById('cribResult');
if (ct.length < 2 || crib.length < 1) { out.textContent='Enter ciphertext and a crib word.'; return; }
if (crib.length > ct.length) { out.textContent='Crib is longer than ciphertext.'; return; }
let html = '<div style="display:flex;flex-direction:column;gap:.4rem;max-height:300px;overflow-y:auto;">';
for (let pos = 0; pos <= ct.length - crib.length; pos++) {
let key = '';
for (let j = 0; j < crib.length; j++) {
key += ALPHA[(ct.charCodeAt(pos + j) - crib.charCodeAt(j) + 26) % 26];
}
const period = fragmentPeriod(key);
const style = period !== null ? 'border-color:var(--gold);background:var(--gold-glow);' : '';
html += '<div style="font-family:var(--fm);font-size:.75rem;background:var(--s3);border:1px solid var(--s4);border-radius:var(--r);padding:.5rem .75rem;color:var(--tx2);' + style + '">';
html += '<span style="color:var(--tx3);min-width:60px;display:inline-block;">Pos ' + pos + ':</span> ';
html += 'Key fragment = <span style="color:var(--gold-lt);font-weight:700;">' + key + '</span>';
if (period !== null) html += ' <span style="color:var(--gold);margin-left:.5rem;">Period ' + period + ' fits this fragment (hypothesis)</span>';
html += '</div>';
}
html += '</div><p style="margin-top:.75rem;color:var(--tx2);">A highlighted period fits at least two repeats within the guessed span. Check the proposed key against plaintext outside the crib. No highlight does not rule out a longer period or a running key.</p>';
out.innerHTML = html;
}
// ── Known Plaintext Attack ──────────────────────────
function runKPA() {
const plain = document.getElementById('kpaPlain').value.toUpperCase().replace(/[^A-Z]/g,'');
const cipher = document.getElementById('kpaCipher').value.toUpperCase().replace(/[^A-Z]/g,'');
const out = document.getElementById('kpaResult');
if (!plain.length || !cipher.length) { out.textContent='Enter both plaintext and ciphertext.'; return; }
const len = Math.min(plain.length, cipher.length);
let key = '';
for (let i = 0; i < len; i++) {
key += ALPHA[(cipher.charCodeAt(i) - plain.charCodeAt(i) + 26) % 26];
}
let html = '<strong>Observed keystream (' + len + ' positions):</strong> <span style="color:var(--gold-lt);font-family:var(--fm);letter-spacing:.1em;overflow-wrap:anywhere;">' + key + '</span>';
html += '<br><strong>Supported plaintext span:</strong> <span style="font-family:var(--fm);overflow-wrap:anywhere;">' + plain.slice(0, len) + '</span>';
if (plain.length > cipher.length) html += '<br>Extra plaintext has no matching ciphertext and was not used.';
if (cipher.length > len) html += '<br><strong>Unknown remainder:</strong> ' + (cipher.length - len) + ' ciphertext positions; the observed fragment alone does not decrypt them.';
const period = fragmentPeriod(key);
if (period !== null) {
html += '<br><strong>Period hypothesis:</strong> ' + period + ' (fragment ' + key.slice(0, period) + '). This fits the observed span; it does not prove the complete key repeats.';
} else {
html += '<br>No period with two complete repeats fits the observed span. Longer periods and nonrepeating keys remain possible.';
}
const periodInput = document.getElementById('kpaPeriod');
const requested = periodInput.value.trim();
// Browsers expose unfinished number input (for example "1e") as an empty
// value with badInput set. Do not mistake that state for an omitted period.
if (periodInput.validity.badInput) {
html += '<br><strong>Cannot test that period:</strong> enter a whole number from 1 to ' + len + ', or clear the field.';
} else if (requested) {
const assumed = Number(requested);
if (!Number.isInteger(assumed) || assumed < 1 || assumed > len) {
html += '<br><strong>Cannot test that period:</strong> enter a whole number from 1 to ' + len + '.';
} else {
const candidate = key.slice(0, assumed);
const fits = Array.from(key).every((letter, i) => letter === candidate[i % assumed]);
if (!fits) {
html += '<br><strong>Period ' + assumed + ' rejected:</strong> it contradicts the observed keystream.';
} else {
let extrapolated = '';
for (let i = len; i < cipher.length; i++) {
extrapolated += ALPHA[(cipher.charCodeAt(i) - candidate.charCodeAt(i % assumed) + 26) % 26];
}
html += '<br><br><strong>Assumed key:</strong> ' + candidate + ' (period ' + assumed + ').';
if (extrapolated) html += '<br><strong>Unverified extension under this assumption:</strong> <span style="font-family:var(--fm);overflow-wrap:anywhere;">' + extrapolated + '</span>';
else html += '<br>There is no ciphertext beyond the known span to extend.';
html += '<br>Validate this hypothesis against independent plaintext or other evidence.';
}
}
}
out.innerHTML = html;
}
// The score stays unchanged: this lesson exposes its limitations.
function caesarToyScore(text) {
const letters = text.toUpperCase().replace(/[^A-Z]/g, '');
if (!letters.length) return null;
return Array.from(letters).reduce((sum, c) => sum + ENG_FREQ[c.charCodeAt(0) - 65], 0) / letters.length;
}
function caesarToyDecrypt(text, shift) {
return text.toUpperCase().replace(/[A-Z]/g, c => ALPHA[(c.charCodeAt(0) - 65 - shift + 26) % 26]);
}
const CAESAR_LESSON_EXAMPLES = {
hill: { ciphertext: 'KHOOR ZRUOG', plaintext: 'HELLO WORLD', shift: 3 },
anneal: { ciphertext: 'WKLV LV D WHVW PHVVDJH', plaintext: 'THIS IS A TEST MESSAGE', shift: 3 }
};
function renderCaesarLandscape(prefix, input, state) {
const out = document.getElementById(prefix + 'Landscape');
if (!/[A-Z]/i.test(input)) {
out.textContent = 'Enter at least one A–Z letter to define a score. No search or language conclusion is available.';
return;
}
const rows = Array.from({length: 26}, (_, shift) => ({shift, text: caesarToyDecrypt(input, shift), score: caesarToyScore(caesarToyDecrypt(input, shift))}));
const maximum = Math.max(...rows.map(row => row.score));
const optima = rows.filter(row => Math.abs(row.score - maximum) < 1e-10).map(row => row.shift);
const example = CAESAR_LESSON_EXAMPLES[prefix];
const known = input.toUpperCase().trim() === example.ciphertext ? example : null;
let html = '<h3>All 26 shifts under this objective</h3><p><strong>Search stopping point:</strong> ' +
(state ? 'shift ' + state.stop + ' (last accepted state).' : 'not run.') +
' <strong>Score optimum after checking all 26:</strong> shift' + (optima.length > 1 ? 's ' : ' ') + optima.join(', ') +
'. <strong>Supplied known answer:</strong> ' + (known ? 'shift ' + known.shift + ', ' + known.plaintext + '.' : 'none for this edited input.') + '</p>';
if (state) html += '<p><strong>Best sampled shift:</strong> ' + state.best + '. <strong>Evaluated path:</strong> ' + state.path.join(' → ') + '. Rejected proposals are included; they do not become the stopping point.</p>';
html += '<p>The exhaustive score check is a reference for this 26-key lesson, separate from the search trajectory. It does not validate a plaintext.</p>' +
'<div style="overflow-x:auto;" role="region" tabindex="0" aria-label="Caesar shift scores; scroll horizontally if needed"><table class="caesar-landscape-table" style="width:100%;text-align:left;"><caption>Higher mean letter-frequency scores are preferred. Markers distinguish the search state, model optimum and supplied answer.</caption><thead><tr><th scope="col">Shift</th><th scope="col">Score</th><th scope="col">Role</th><th scope="col">Candidate</th></tr></thead><tbody>';
for (const row of rows) {
const roles = [];
if (state && row.shift === state.stop) roles.push('Stopping point');
if (state && row.shift === state.best) roles.push('Best sampled');
if (optima.includes(row.shift)) roles.push('Score optimum');
if (known && row.shift === known.shift) roles.push('Supplied answer');
html += '<tr data-shift="' + row.shift + '" data-score="' + row.score + '"><th scope="row">' + row.shift + '</th><td>' + row.score.toFixed(4) +
'<br><meter min="0" max="12.7" value="' + row.score + '" aria-label="Shift ' + row.shift + ' score" style="width:6rem;">' + row.score.toFixed(4) + '</meter></td><td>' + roles.join('; ') +
'</td><td style="font-family:var(--fm);word-break:break-word;">' + escapeLabText(row.text) + '</td></tr>';
}
out.innerHTML = html + '</tbody></table></div>';
}
for (const prefix of ['hill', 'anneal']) {
const field = document.getElementById(prefix + 'Input');
function refresh() { document.getElementById(prefix + 'Output').textContent = ''; renderCaesarLandscape(prefix, field.value); }
field.addEventListener('input', refresh);
document.getElementById(prefix + 'Example').addEventListener('click', () => { field.value = CAESAR_LESSON_EXAMPLES[prefix].ciphertext; refresh(); });
refresh();
}
// ── Hill Climbing ───────────────────────────────────
async function runHillClimb() {
const input = document.getElementById('hillInput').value.toUpperCase();
const out = document.getElementById('hillOutput');
const btn = document.getElementById('hillBtn');
if (!/[A-Z]/.test(input)) { out.textContent = ''; renderCaesarLandscape('hill', input); return; }
document.getElementById('hillInput').disabled = true;
document.getElementById('hillExample').disabled = true;
btn.disabled = true;
out.innerHTML = '';
const score = caesarToyScore;
const decrypt = caesarToyDecrypt;
let current = 0;
let bestScore = score(decrypt(input, 0));
let steps = [{shift: 0, score: bestScore, text: decrypt(input, 0), action: 'Start'}];
for (let iter = 0; iter < 30; iter++) {
const neighbors = [(current + 1) % 26, (current + 25) % 26];
let improved = false;
for (const n of neighbors) {
const ns = score(decrypt(input, n));
steps.push({shift: n, score: ns, text: decrypt(input, n), action: ns > bestScore ? 'Climb ↑' : 'Reject ↓'});
if (ns > bestScore) {
bestScore = ns;
current = n;
improved = true;
break;
}
}
if (!improved) break;
}
for (let i = 0; i < steps.length; i++) {
const s = steps[i];
const line = document.createElement('div');
const color = s.action === 'Climb ↑' ? 'var(--green)' : s.action === 'Reject ↓' ? 'var(--red)' : 'var(--gold)';
line.style.cssText = 'padding:.3rem .6rem;border-radius:3px;';
line.innerHTML = '<span style="color:' + color + ';min-width:70px;display:inline-block;">' + s.action + '</span> Shift ' + s.shift.toString().padStart(2,'0') + ' (score ' + s.score.toFixed(1) + '): <span style="color:var(--tx);">' + escapeLabText(s.text) + '</span>';
if (i === steps.length - 1 && s.action !== 'Reject ↓') {
line.style.background = 'var(--gold-glow)';
line.innerHTML += ' <span style="color:var(--green);font-size:.7rem;margin-left:.5rem;">← Converged</span>';
}
out.appendChild(line);
await new Promise(r => setTimeout(r, 120));
}
renderCaesarLandscape('hill', input, {stop: current, best: current, path: steps.map(step => step.shift)});
document.getElementById('hillInput').disabled = false;
document.getElementById('hillExample').disabled = false;
btn.disabled = false;
}
// ── Simulated Annealing ─────────────────────────────
async function runAnneal() {
const input = document.getElementById('annealInput').value.toUpperCase();
const out = document.getElementById('annealOutput');
const btn = document.getElementById('annealBtn');
if (!/[A-Z]/.test(input)) { out.textContent = ''; renderCaesarLandscape('anneal', input); return; }
document.getElementById('annealInput').disabled = true;
document.getElementById('annealExample').disabled = true;
btn.disabled = true;
out.innerHTML = '';
const score = caesarToyScore;
const decrypt = caesarToyDecrypt;
let current = Math.floor(Math.random() * 26);
const initialShift = current;
let currentScore = score(decrypt(input, current));
let bestShift = current, bestScore = currentScore;
let temp = 10.0;
const cooling = 0.85;
let steps = [];