Merge branch 'master' of git.njae.me.uk:national-cipher-challenge
authorNeil Smith <neil.git@njae.me.uk>
Thu, 17 Oct 2013 14:40:18 +0000 (15:40 +0100)
committerNeil Smith <neil.git@njae.me.uk>
Thu, 17 Oct 2013 14:40:18 +0000 (15:40 +0100)
2013/1a.ciphertext [new file with mode: 0644]
2013/1a.plaintext [new file with mode: 0644]
2013/1b.ciphertext [new file with mode: 0644]
2013/1b.plaintext [new file with mode: 0644]
cipher.py

diff --git a/2013/1a.ciphertext b/2013/1a.ciphertext
new file mode 100644 (file)
index 0000000..aefe481
--- /dev/null
@@ -0,0 +1,13 @@
+LMIZ XPQT, 
+
+PWE KWCTL Q XIAA CX BPM WXXWZBCVQBG BW EWZS WV BPQA? Q PIL I KZIKS IB IVWBPMZ WN BPM VWBMA GWC NWCVL QV UWVBUIZBZM. QB TWWSA TQSM BPM WTLMAB QBMU QV BPM XIKSMB IVL Q BPWCOPB QB UQOPB JM I OWWL XTIKM BW ABIZB. QB UISMA AWUM AMVAM WN BPM JCKPMVEITL ZMNMZMVKM QV BPM TIBMZ VWBM GWC AMVB. 
+
+QB LQLV'B MFXTIQV BPM XIZQA TQVS AW Q AMVB I BMIU QVBW BPIB VMQOPJWCZPWWL BW OIBPMZ QVBMT JCB BPMG LQLV'B KWUM CX EQBP DMZG UCKP. EM LQL OMB WVM ZMXWZB BPIB BPM PWCAM PIL JMMV WEVML JG I OMZUIV NIUQTG JMNWZM BPM EIZ, JCB BPIB QB PIL JMMV BISMV WDMZ JG IV AA WNNQKMZ QV VQVMBMMV NWZBG WVM. ACZMBM ZMKWZLA ACOOMAB BPIB BPM NIUQTG KIUM NZWU EMQUIZ QV ICOCAB VQVMBMMV BPQZBG AMDMV, EPQKP QA ACOOMABQDM OQDMV BPM BQUQVO IVL BPM OMWOZIXPG, AW Q PIDM AMVB BPM BMIU BW JCKPMVEITL BW AMM EPIB BPMG KIV NQVL. 
+
+Q IU RCUXQVO BW KWVKTCAQWVA PMZM, JCB BPM XWZBZIQB AIZIP UMVBQWVA PIA BW JM BPM UWVI TQAI. Q PIDM JMMV BZGQVO BW OMB IKKMAA BW QB, JCB BPM NZMVKP ICBPWZQBQMA IZM AXWWSML. BPM BPMNB WN BPM XIQVBQVO JG XMZCOOQI JIKS QV VQVMBMMV MTMDMV PIA UILM BPMU DMZG AMVAQBQDM. BPM VIUM KPICLZWV EIA UMVBQWVML UWZM BPIV WVKM, IVL Q VMML AWUM BQUM BW TWWS QVBW BPM PQABWZG. 
+
+QN GWC PIDM IVG QVNTCMVKM IB ITT IB BPM UCAMCU Q BPQVS EM VMML I XZWXMZ MFIUQVIBQWV WN BPM XIQVBQVO, IVL Q VMML BW SVWE EPIB PIXXMVML BW QB LCZQVO BPM EIZ. 
+
+ITT BPM JMAB, 
+
+PIZZG
\ No newline at end of file
diff --git a/2013/1a.plaintext b/2013/1a.plaintext
new file mode 100644 (file)
index 0000000..da08fbf
--- /dev/null
@@ -0,0 +1,13 @@
+DEAR PHIL, 
+
+HOW COULD I PASS UP THE OPPORTUNITY TO WORK ON THIS? I HAD A CRACK AT ANOTHER OF THE NOTES YOU FOUND IN MONTMARTRE. IT LOOKS LIKE THE OLDEST ITEM IN THE PACKET AND I THOUGHT IT MIGHT BE A GOOD PLACE TO START. IT MAKES SOME SENSE OF THE BUCHENWALD REFERENCE IN THE LATER NOTE YOU SENT. 
+
+IT DIDN'T EXPLAIN THE PARIS LINK SO I SENT A TEAM INTO THAT NEIGHBOURHOOD TO GATHER INTEL BUT THEY DIDN'T COME UP WITH VERY MUCH. WE DID GET ONE REPORT THAT THE HOUSE HAD BEEN OWNED BY A GERMAN FAMILY BEFORE THE WAR, BUT THAT IT HAD BEEN TAKEN OVER BY AN SS OFFICER IN NINETEEN FORTY ONE. SURETE RECORDS SUGGEST THAT THE FAMILY CAME FROM WEIMAR IN AUGUST NINETEEN THIRTY SEVEN, WHICH IS SUGGESTIVE GIVEN THE TIMING AND THE GEOGRAPHY, SO I HAVE SENT THE TEAM TO BUCHENWALD TO SEE WHAT THEY CAN FIND. 
+
+I AM JUMPING TO CONCLUSIONS HERE, BUT THE PORTRAIT SARAH MENTIONS HAS TO BE THE MONA LISA. I HAVE BEEN TRYING TO GET ACCESS TO IT, BUT THE FRENCH AUTHORITIES ARE SPOOKED. THE THEFT OF THE PAINTING BY PERUGGIA BACK IN NINETEEN ELEVEN HAS MADE THEM VERY SENSITIVE. THE NAME CHAUDRON WAS MENTIONED MORE THAN ONCE, AND I NEED SOME TIME TO LOOK INTO THE HISTORY. 
+
+IF YOU HAVE ANY INFLUENCE AT ALL AT THE MUSEUM I THINK WE NEED A PROPER EXAMINATION OF THE PAINTING, AND I NEED TO KNOW WHAT HAPPENED TO IT DURING THE WAR. 
+
+ALL THE BEST, 
+
+HARRY
\ No newline at end of file
diff --git a/2013/1b.ciphertext b/2013/1b.ciphertext
new file mode 100644 (file)
index 0000000..9e551da
--- /dev/null
@@ -0,0 +1,3 @@
+YWBRBSGG WG HC PS TCIBR SJSFMKVSFS, SJSB WB HVWG GHWBYWBU DZOQS. HVS UIOFRG HOYS OKOM OBMHVWBU HVSM QOB HVOH AWUVH PFWBU IG GCAS XCM, PIH HVSM QOB'H HOYS SJSFMHVWBU. GCAS CT HVS UWFZG GWBU, CHVSFG HSZZ GHCFWSG HC YSSD HVS TSOF OH HVS SRUS CT CIF VSOFHG - WH BSJSF QCADZSHSZM ZSOJSG IG - OBR AM DWQHIFSG OFS OZZ W VOJS HC CTTSF WB FSHIFB. DODSF WG DFSQWCIG OBR ACGH CT AM DOWBHWBUG OBR GYSHQVSG OFS VWRRSB CB HVS FCIUV KCCRSB GZOHG CT CIF PIBYG IBRSF HVS HVWB AOHHFSGGSG. RSHOWZ WG WADCGGWPZS, PIH KCIZR PS VOFR SJSB CB TWBS QOBJOG UWJSB HVS DWUASBHG W QOB AOYS TFCA HVS PFWQY RIGH, GCWZ OBR GQFIPPM KSSRG WB HVS QOAD. W OA HVOBYTIZ SJSFMROM TCF AM UFOBRTOHVSF'G WBGWGHSBQS HVOH OB OFHWGH GVCIZR PS OPZS HC AOYS HVSWF CKB QCZCIFG. KVSB W RC TWBR DODSF, W KFWHS. HVWG UFSOGM GQFOD KOG HVS KFODDWBU CB O UIOFR'G GOBRKWQV OBR HVS UFSOGS KCIZR AOYS WH WADCGGWPZS HC DOWBH CB SJSB WT W KOBHSR HC, PIH QVOFQCOZ TOZZSB TFCA O UIOFR'G PFONWSF GSSAG HC KCFY TWBS. 
+
+HVS CHVSFG WB AM RCFAWHCFM HSZZ AS HC FSQCFR OG AIQV OG W QOB. BCBS CT IG YBCK WT KS KWZZ ZWJS HC HSZZ HVS KCFZR CT HVS HSFFWPZS HVWBUG HVOH VODDSB VSFS, GC W RFOK OBR, KVSB W QOB, W KFWHS, OZKOMG KWHV CBS SOF ZWGHSBWBU TCF HVS QFIBQV CT O PCCH CB HVS UFOJSZ CIHGWRS. HVS GHCBSG OFS GVOFD OBR HVS GCZRWSFG RCB'H UWJS IG TCCHKSOF. WH WG OBCHVSF KOM HC VIAWZWOHS IG OBR KSOF IG RCKB, PIH HVSM RC BCH FSOZWGS HVOH HVWG ASOBG HVOH KS QOB OZKOMG HSZZ KVSB HVSM OFS QCAWBU. YWBRBSGG WG HC PS TCIBR SJSFMKVSFS VSFS, SJSB WB HVSWF GHIDWR QFISZHM. 
\ No newline at end of file
diff --git a/2013/1b.plaintext b/2013/1b.plaintext
new file mode 100644 (file)
index 0000000..1e22a0f
--- /dev/null
@@ -0,0 +1,3 @@
+KINDNESS IS TO BE FOUND EVERYWHERE, EVEN IN THIS STINKING PLACE. THE GUARDS TAKE AWAY ANYTHING THEY CAN THAT MIGHT BRING US SOME JOY, BUT THEY CAN'T TAKE EVERYTHING. SOME OF THE GIRLS SING, OTHERS TELL STORIES TO KEEP THE FEAR AT THE EDGE OF OUR HEARTS - IT NEVER COMPLETELY LEAVES US - AND MY PICTURES ARE ALL I HAVE TO OFFER IN RETURN. PAPER IS PRECIOUS AND MOST OF MY PAINTINGS AND SKETCHES ARE HIDDEN ON THE ROUGH WOODEN SLATS OF OUR BUNKS UNDER THE THIN MATTRESSES. DETAIL IS IMPOSSIBLE, BUT WOULD BE HARD EVEN ON FINE CANVAS GIVEN THE PIGMENTS I CAN MAKE FROM THE BRICK DUST, SOIL AND SCRUBBY WEEDS IN THE CAMP. I AM THANKFUL EVERYDAY FOR MY GRANDFATHER'S INSISTENCE THAT AN ARTIST SHOULD BE ABLE TO MAKE THEIR OWN COLOURS. WHEN I DO FIND PAPER, I WRITE. THIS GREASY SCRAP WAS THE WRAPPING ON A GUARD'S SANDWICH AND THE GREASE WOULD MAKE IT IMPOSSIBLE TO PAINT ON EVEN IF I WANTED TO, BUT CHARCOAL FALLEN FROM A GUARD'S BRAZIER SEEMS TO WORK FINE. 
+
+THE OTHERS IN MY DORMITORY TELL ME TO RECORD AS MUCH AS I CAN. NONE OF US KNOW IF WE WILL LIVE TO TELL THE WORLD OF THE TERRIBLE THINGS THAT HAPPEN HERE, SO I DRAW AND, WHEN I CAN, I WRITE, ALWAYS WITH ONE EAR LISTENING FOR THE CRUNCH OF A BOOT ON THE GRAVEL OUTSIDE. THE STONES ARE SHARP AND THE SOLDIERS DON'T GIVE US FOOTWEAR. IT IS ANOTHER WAY TO HUMILIATE US AND WEAR US DOWN, BUT THEY DO NOT REALISE THAT THIS MEANS THAT WE CAN ALWAYS TELL WHEN THEY ARE COMING. KINDNESS IS TO BE FOUND EVERYWHERE HERE, EVEN IN THEIR STUPID CRUELTY. 
\ No newline at end of file
index c4b28ba321ea4a55953af22f914c5b94a7843b6f..b07cd250fb8263e338a1b35686f28a583f9bf2a2 100644 (file)
--- a/cipher.py
+++ b/cipher.py
@@ -1,6 +1,36 @@
 import string
 import collections
 
+english_counts = collections.defaultdict(int)
+with open('count_1l.txt', 'r') as f:
+    for line in f:
+        (letter, count) = line.split("\t")
+        english_counts[letter] = int(count)
+
+def sanitise(text):
+    sanitised = [c.lower() for c in text if c in string.ascii_letters]
+    return ''.join(sanitised)
+
+def letter_frequencies(message):
+    frequencies = collections.defaultdict(int)
+    for letter in sanitise(message): 
+        frequencies[letter]+=1
+    return frequencies
+
+def scale_freq(frequencies):
+    total= sum(frequencies.values())
+    scaled_frequencies = collections.defaultdict(int)
+    for letter in frequencies.keys():
+        scaled_frequencies[letter] = frequencies[letter] / total
+    return scaled_frequencies
+
+def value_diff(frequencies1, frequencies2):
+    total= 0
+    for letter in frequencies1.keys():
+        total += abs(frequencies1[letter]-frequencies2[letter])
+    return total
+        
+    
 
 def caesar_cipher_letter(letter, shift):
     if letter in string.ascii_letters:
@@ -23,10 +53,16 @@ def caesar_cipher_message(message, shift):
 def caesar_decipher_message(message, shift):
     return caesar_cipher_message(message, -shift)
 
-def letter_frequencies(message):
-    frequencies = collections.defaultdict(int)
-    for letter in message: 
-        if letter in  string.ascii_letters:
-            frequencies[letter.lower()]+=1
-    return frequencies
-
+def caesar_break(message):
+    best_key = 0
+    best_fit = float("inf")
+    for shift in range(26):
+        plaintxt = caesar_decipher_message(message, shift)
+        lettertxt = letter_frequencies(plaintxt)
+        total1 = scale_freq(lettertxt)
+        total2 = scale_freq(english_counts)
+        fit = value_diff(total2, total1)
+        if fit < best_fit:
+            best_key = shift
+            best_fit = fit
+    return best_key