Done Cadenus ciphers, but not automated breaking
[cipher-tools.git] / cadenus-ciphers.ipynb
diff --git a/cadenus-ciphers.ipynb b/cadenus-ciphers.ipynb
new file mode 100644 (file)
index 0000000..40b4dc4
--- /dev/null
@@ -0,0 +1,542 @@
+{
+ "metadata": {
+  "name": "",
+  "signature": "sha256:c52a83147f45ed889200fe19a5d4ec4422e884758e7d8a11536fbaafa6c627ee"
+ },
+ "nbformat": 3,
+ "nbformat_minor": 0,
+ "worksheets": [
+  {
+   "cells": [
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "import matplotlib.pyplot as plt\n",
+      "import pandas as pd\n",
+      "import collections\n",
+      "import string\n",
+      "import numpy as np\n",
+      "from numpy import matrix\n",
+      "from numpy import linalg\n",
+      "%matplotlib inline\n",
+      "\n",
+      "from cipher import *\n",
+      "from cipherbreak import *\n",
+      "\n",
+      "c8a = open('2014/8a.ciphertext').read().strip()\n",
+      "c8b = open('2014/8b.ciphertext').read().strip()"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [],
+     "prompt_number": 4
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "def cadenus_letter(n, doubled='v'):\n",
+      "    letter = chr(n + ord('a'))\n",
+      "    if letter > doubled:\n",
+      "        letter = chr(n + ord('a') + 1)\n",
+      "    return letter"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [],
+     "prompt_number": 2
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "c8bl = ''.join([cadenus_letter(int(c, 2)) for c in chunks(c8b, 5)])\n",
+      "c8bl"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 5,
+       "text": [
+        "'afcaeuottacthrioletcserthshtrahkzorpfrgeoadppjnglternefeofiortsddoeeumscruernfetlaafstxientrvoonerhuahravereetsvsielhlostdoalozaesmnndignnrhohhtsnaoilncnssicreanneeiiierxtanesrvogieizxssdgpvoiaisaoaeoaedrnitrnyeigrpsshadhdtoipaateyennesagrobtlesrnroirzpbgedcllixalaleenigrrnxzrlimlpstoleftrdmuarieeeiiaolnexsaohrtlstobetnslvfivdovtpoaeeisciohipseveedtexfarnhebleaotohtttepnckaonhxetmvzprreonnasgdedoeeeoaamtcicttifnadresrtserosetrhcictpsaaehldhsfysoaotctbbsoeirnsadlztrrunrceptthreuhnktaceceelrxnireeeaeseeeidisogceomnrtejhagabsenitlxtrnbmielsaretesrngsnhebiosdienafleisahocifevmfatanatrniagnhatnmibniufenrtottrnzpaidziegdnmerhhiotretcesseildrbceprigaesoadltahievebrcenlevasadnnthneiteiisahuhhuamonefzhlonxhaeeeeosneezaneisetogziterlihtcmioirarfdoetnihtnehiikamrdmnadanaodseseizclsiantaoltcizmidentthltndytttmasbleaeetlisirtxturpfailteaoefeisiiizisikvtxisprbsinelphrmohiagnlslvitodaisdpnzddcaaotahcehtueirredaectosnrhvnaodoikoetcineneurrisdcouraglvimmuppditeanditmaaiaieleonnreedaodboiumelrotntttgitnrlrienniklzsogstcifzpipvidvssmnceiasiitsnneatitomrhbnhnidprlrepoznalsnvsdosanesitfaenltgodatteeaisicrootmsmfhauenirsghznxeintegodiileedtarnosrcaaendtcuttfdrbehtmfitoordruiaozaanoeeldoinhusgiteaoriecevemntratmtfpeucutahamtnexonicdeemrpaolitoafesoosspfnlneeootachllirssysofpdftfrnpraeeazlonahautntcntcbaxloneftoatecvoxdlxvnneedtiioigtegmtaheeatefaaeprrcrosheerrpalediengidrreouhvesuroztnsosinuiuiofprda'"
+       ]
+      }
+     ],
+     "prompt_number": 5
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "c8bgrid = chunks(c8bl, int(len(c8bl) / 25))\n",
+      "c8bgrid"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 9,
+       "text": [
+        "['afcaeuottacthrioletcserthshtrahkzorpfrgeoadppjnglternefe',\n",
+        " 'ofiortsddoeeumscruernfetlaafstxientrvoonerhuahravereetsv',\n",
+        " 'sielhlostdoalozaesmnndignnrhohhtsnaoilncnssicreanneeiiie',\n",
+        " 'rxtanesrvogieizxssdgpvoiaisaoaeoaedrnitrnyeigrpsshadhdto',\n",
+        " 'ipaateyennesagrobtlesrnroirzpbgedcllixalaleenigrrnxzrlim',\n",
+        " 'lpstoleftrdmuarieeeiiaolnexsaohrtlstobetnslvfivdovtpoaee',\n",
+        " 'isciohipseveedtexfarnhebleaotohtttepnckaonhxetmvzprreonn',\n",
+        " 'asgdedoeeeoaamtcicttifnadresrtserosetrhcictpsaaehldhsfys',\n",
+        " 'oaotctbbsoeirnsadlztrrunrceptthreuhnktaceceelrxnireeeaes',\n",
+        " 'eeeidisogceomnrtejhagabsenitlxtrnbmielsaretesrngsnhebios',\n",
+        " 'dienafleisahocifevmfatanatrniagnhatnmibniufenrtottrnzpai',\n",
+        " 'dziegdnmerhhiotretcesseildrbceprigaesoadltahievebrcenlev',\n",
+        " 'asadnnthneiteiisahuhhuamonefzhlonxhaeeeeosneezaneisetogz',\n",
+        " 'iterlihtcmioirarfdoetnihtnehiikamrdmnadanaodseseizclsian',\n",
+        " 'taoltcizmidentthltndytttmasbleaeetlisirtxturpfailteaoefe',\n",
+        " 'isiiizisikvtxisprbsinelphrmohiagnlslvitodaisdpnzddcaaota',\n",
+        " 'hcehtueirredaectosnrhvnaodoikoetcineneurrisdcouraglvimmu',\n",
+        " 'ppditeanditmaaiaieleonnreedaodboiumelrotntttgitnrlrienni',\n",
+        " 'klzsogstcifzpipvidvssmnceiasiitsnneatitomrhbnhnidprlrepo',\n",
+        " 'znalsnvsdosanesitfaenltgodatteeaisicrootmsmfhauenirsghzn',\n",
+        " 'xeintegodiileedtarnosrcaaendtcuttfdrbehtmfitoordruiaozaa',\n",
+        " 'noeeldoinhusgiteaoriecevemntratmtfpeucutahamtnexonicdeem',\n",
+        " 'rpaolitoafesoosspfnlneeootachllirssysofpdftfrnpraeeazlon',\n",
+        " 'ahautntcntcbaxloneftoatecvoxdlxvnneedtiioigtegmtaheeatef',\n",
+        " 'aaeprrcrosheerrpalediengidrreouhvesuroztnsosinuiuiofprda']"
+       ]
+      }
+     ],
+     "prompt_number": 9
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "len(c8bgrid[0])"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 11,
+       "text": [
+        "56"
+       ]
+      }
+     ],
+     "prompt_number": 11
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "def make_keycolumn(doubled_letters = 'vw', start='a', reverse=False):\n",
+      "    index_to_remove = string.ascii_lowercase.find(doubled_letters[0])\n",
+      "    short_alphabet = string.ascii_lowercase[:index_to_remove] + string.ascii_lowercase[index_to_remove+1:]\n",
+      "    if reverse:\n",
+      "        short_alphabet = ''.join(reversed(short_alphabet))\n",
+      "    start_pos = short_alphabet.find(start)\n",
+      "    rotated_alphabet = short_alphabet[start_pos:] + short_alphabet[:start_pos]\n",
+      "    keycolumn = {l: i for i, l in enumerate(rotated_alphabet)}\n",
+      "    keycolumn[doubled_letters[0]] = keycolumn[doubled_letters[1]]\n",
+      "    return keycolumn"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [],
+     "prompt_number": 75
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "pt = sanitise(\"Whoever has made a voyage up the Hudson must remember the Kaatskill mountains. They are a dismembered branch of the great\")\n",
+      "keyword = 'wink'"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [],
+     "prompt_number": 36
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "keycolumn = make_keycolumn(reverse=True)\n",
+      "[(k, keycolumn[k]) for k in sorted(keycolumn)]"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 103,
+       "text": [
+        "[('a', 0),\n",
+        " ('b', 24),\n",
+        " ('c', 23),\n",
+        " ('d', 22),\n",
+        " ('e', 21),\n",
+        " ('f', 20),\n",
+        " ('g', 19),\n",
+        " ('h', 18),\n",
+        " ('i', 17),\n",
+        " ('j', 16),\n",
+        " ('k', 15),\n",
+        " ('l', 14),\n",
+        " ('m', 13),\n",
+        " ('n', 12),\n",
+        " ('o', 11),\n",
+        " ('p', 10),\n",
+        " ('q', 9),\n",
+        " ('r', 8),\n",
+        " ('s', 7),\n",
+        " ('t', 6),\n",
+        " ('u', 5),\n",
+        " ('v', 4),\n",
+        " ('w', 4),\n",
+        " ('x', 3),\n",
+        " ('y', 2),\n",
+        " ('z', 1)]"
+       ]
+      }
+     ],
+     "prompt_number": 103
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "keycolumn = make_keycolumn(doubled_letters='ij', start='b', reverse=True)\n",
+      "[(k, keycolumn[k]) for k in sorted(keycolumn)]"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 101,
+       "text": [
+        "[('a', 1),\n",
+        " ('b', 0),\n",
+        " ('c', 24),\n",
+        " ('d', 23),\n",
+        " ('e', 22),\n",
+        " ('f', 21),\n",
+        " ('g', 20),\n",
+        " ('h', 19),\n",
+        " ('i', 18),\n",
+        " ('j', 18),\n",
+        " ('k', 17),\n",
+        " ('l', 16),\n",
+        " ('m', 15),\n",
+        " ('n', 14),\n",
+        " ('o', 13),\n",
+        " ('p', 12),\n",
+        " ('q', 11),\n",
+        " ('r', 10),\n",
+        " ('s', 9),\n",
+        " ('t', 8),\n",
+        " ('u', 7),\n",
+        " ('v', 6),\n",
+        " ('w', 5),\n",
+        " ('x', 4),\n",
+        " ('y', 3),\n",
+        " ('z', 2)]"
+       ]
+      }
+     ],
+     "prompt_number": 101
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "pt_rows = chunks(pt, len(pt) // 25, fillvalue='a')\n",
+      "pt_rows"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 15,
+       "text": [
+        "['whoe',\n",
+        " 'verh',\n",
+        " 'asma',\n",
+        " 'deav',\n",
+        " 'oyag',\n",
+        " 'eupt',\n",
+        " 'hehu',\n",
+        " 'dson',\n",
+        " 'must',\n",
+        " 'reme',\n",
+        " 'mber',\n",
+        " 'thek',\n",
+        " 'aats',\n",
+        " 'kill',\n",
+        " 'moun',\n",
+        " 'tain',\n",
+        " 'sthe',\n",
+        " 'yare',\n",
+        " 'adis',\n",
+        " 'memb',\n",
+        " 'ered',\n",
+        " 'bran',\n",
+        " 'chof',\n",
+        " 'theg',\n",
+        " 'reat']"
+       ]
+      }
+     ],
+     "prompt_number": 15
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "[keycolumn[l] for l in keyword]"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 38,
+       "text": [
+        "[21, 8, 13, 10]"
+       ]
+      }
+     ],
+     "prompt_number": 38
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "pt_columns = [''.join(c) for c in zip(*pt_rows)]\n",
+      "pt_columns"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 47,
+       "text": [
+        "['wvadoehdmrmtakmtsyamebctr',\n",
+        " 'heseyuesuebhaioataderrhhe',\n",
+        " 'ormaaphosmeetluihrimeaoea',\n",
+        " 'ehavgtunterkslnneesbdnfgt']"
+       ]
+      }
+     ],
+     "prompt_number": 47
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "rotated_pt_columns = [''.join(col[start:] + col[:start]) for start, col in zip([keycolumn[l] for l in keyword], pt_columns)]    \n",
+      "rotated_pt_rows = zip(*rotated_pt_columns)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [],
+     "prompt_number": 78
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "transpositions = transpositions_of(keyword)\n",
+      "transposed = [transpose(r, transpositions) for r in rotated_pt_rows]\n",
+      "transposed"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 79,
+       "text": [
+        "[['a', 'n', 't', 'o'],\n",
+        " ['d', 'e', 'l', 'e'],\n",
+        " ['e', 'e', 'u', 'h'],\n",
+        " ['r', 's', 'i', 'd'],\n",
+        " ['r', 'b', 'h', 'm'],\n",
+        " ['h', 'd', 'r', 'r'],\n",
+        " ['h', 'n', 'i', 'm'],\n",
+        " ['e', 'f', 'm', 't'],\n",
+        " ['h', 'g', 'e', 'a'],\n",
+        " ['e', 't', 'a', 'k'],\n",
+        " ['s', 'e', 'o', 'm'],\n",
+        " ['e', 'h', 'e', 't'],\n",
+        " ['y', 'a', 'a', 's'],\n",
+        " ['u', 'v', 'o', 'y'],\n",
+        " ['e', 'g', 'r', 'a'],\n",
+        " ['s', 't', 'm', 'm'],\n",
+        " ['u', 'u', 'a', 'e'],\n",
+        " ['e', 'n', 'a', 'b'],\n",
+        " ['b', 't', 'p', 'c'],\n",
+        " ['h', 'e', 'h', 't'],\n",
+        " ['a', 'r', 'o', 'r'],\n",
+        " ['i', 'k', 's', 'w'],\n",
+        " ['o', 's', 'm', 'v'],\n",
+        " ['a', 'l', 'e', 'a'],\n",
+        " ['t', 'n', 'e', 'd']]"
+       ]
+      }
+     ],
+     "prompt_number": 79
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "def cadenus_encipher(message, keyword, keycolumn, fillvalue='a'):\n",
+      "    rows = chunks(message, len(message) // 25, fillvalue=fillvalue)\n",
+      "    columns = zip(*rows)\n",
+      "    rotated_columns = [col[start:] + col[:start] for start, col in zip([keycolumn[l] for l in keyword], columns)]    \n",
+      "    rotated_rows = zip(*rotated_columns)\n",
+      "    transpositions = transpositions_of(keyword)\n",
+      "    transposed = [transpose(r, transpositions) for r in rotated_rows]\n",
+      "    return ''.join(chain(*transposed))"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [],
+     "prompt_number": 82
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "cadenus_encipher(pt, 'wink', make_keycolumn(reverse=True))"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 104,
+       "text": [
+        "'antodeleeeuhrsidrbhmhdrrhnimefmthgeaetakseomehetyaasuvoyegrastmmuuaeenabbtpchehtarorikswosmvaleatned'"
+       ]
+      }
+     ],
+     "prompt_number": 104
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "cadenus_encipher(sanitise('a severe limitation on the usefulness of the cadenus ' \n",
+      "                          'is that every message must be a multiple of twenty-five '\n",
+      "                          'letters long'), 'easy', make_keycolumn(reverse=True))"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 105,
+       "text": [
+        "'systretomtattlusoatleeesfiyheasdfnmschbhneuvsnpmtofarenuseieeieltarlmentieetogevesitfaisltngeeuvowul'"
+       ]
+      }
+     ],
+     "prompt_number": 105
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "def cadenus_decipher(message, keyword, keycolumn, fillvalue='a'):\n",
+      "    rows = chunks(message, len(message) // 25, fillvalue=fillvalue)\n",
+      "    transpositions = transpositions_of(keyword)\n",
+      "    untransposed_rows = [untranspose(r, transpositions) for r in rows]\n",
+      "    columns = zip(*untransposed_rows)\n",
+      "    rotated_columns = [col[-start:] + col[:-start] for start, col in zip([keycolumn[l] for l in keyword], columns)]    \n",
+      "    rotated_rows = zip(*rotated_columns)\n",
+      "    # return rotated_columns\n",
+      "    return ''.join(chain(*rotated_rows))"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [],
+     "prompt_number": 93
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "    cadenus_decipher('antodeleeeuhrsidrbhmhdrrhnimefmthgeaetakseomehetyaasuvoyegrastmmuuaeenabbtpchehtarorikswosmvaleatned',\n",
+      "                 'wink',\n",
+      "                 make_keycolumn(reverse=True))"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 106,
+       "text": [
+        "'whoeverhasmadeavoyageupthehudsonmustrememberthekaatskillmountainstheyareadismemberedbranchofthegreat'"
+       ]
+      }
+     ],
+     "prompt_number": 106
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "    cadenus_decipher('systretomtattlusoatleeesfiyheasdfnmschbhneuvsnpmtofarenuseieeieltarlmentieetogevesitfaisltngeeuvowul',\n",
+      "                 'easy',\n",
+      "                 make_keycolumn(reverse=True))"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 107,
+       "text": [
+        "'aseverelimitationontheusefulnessofthecadenusisthateverymessagemustbeamultipleoftwentyfiveletterslong'"
+       ]
+      }
+     ],
+     "prompt_number": 107
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    }
+   ],
+   "metadata": {}
+  }
+ ]
+}
\ No newline at end of file