From dd5d457b15c1951546bd0bf8ce5d6408d9d0f1e8 Mon Sep 17 00:00:00 2001 From: Karl Berry Date: Tue, 9 Apr 2013 17:43:05 +0000 Subject: enigma (8apr13) git-svn-id: svn://tug.org/texlive/trunk@29802 c570f23f-e606-0410-a88d-b1316a301751 --- .../doc/context/third/enigma/enigma/COPYING | 22 + .../doc/context/third/enigma/enigma/README | 29 + .../doc/context/third/enigma/enigma/enigma-doc.pdf | Bin 0 -> 265288 bytes .../context/third/enigma/enigma/enigma_manual.tex | 498 ++++++ .../enigma/examples/enigma-example-context.tex | 59 + .../enigma/examples/enigma-example-latex.tex | 53 + .../enigma/examples/enigma-example-plain.tex | 50 + .../third/enigma/mtx-t-enigma.lua/mtx-t-enigma.lua | 72 + .../third/enigma/t-enigma.mkv/t-enigma.mkvi | 157 ++ Master/texmf-dist/tex/generic/enigma/enigma.lua | 1662 ++++++++++++++++++++ Master/texmf-dist/tex/latex/enigma/enigma.sty | 7 + Master/texmf-dist/tex/plain/enigma/enigma.tex | 130 ++ 12 files changed, 2739 insertions(+) create mode 100644 Master/texmf-dist/doc/context/third/enigma/enigma/COPYING create mode 100644 Master/texmf-dist/doc/context/third/enigma/enigma/README create mode 100644 Master/texmf-dist/doc/context/third/enigma/enigma/enigma-doc.pdf create mode 100644 Master/texmf-dist/doc/context/third/enigma/enigma/enigma_manual.tex create mode 100644 Master/texmf-dist/doc/context/third/enigma/enigma/examples/enigma-example-context.tex create mode 100644 Master/texmf-dist/doc/context/third/enigma/enigma/examples/enigma-example-latex.tex create mode 100644 Master/texmf-dist/doc/context/third/enigma/enigma/examples/enigma-example-plain.tex create mode 100644 Master/texmf-dist/scripts/context/lua/third/enigma/mtx-t-enigma.lua/mtx-t-enigma.lua create mode 100644 Master/texmf-dist/tex/context/third/enigma/t-enigma.mkv/t-enigma.mkvi create mode 100644 Master/texmf-dist/tex/generic/enigma/enigma.lua create mode 100644 Master/texmf-dist/tex/latex/enigma/enigma.sty create mode 100644 Master/texmf-dist/tex/plain/enigma/enigma.tex (limited to 'Master/texmf-dist') diff --git a/Master/texmf-dist/doc/context/third/enigma/enigma/COPYING b/Master/texmf-dist/doc/context/third/enigma/enigma/COPYING new file mode 100644 index 00000000000..dad85d026eb --- /dev/null +++ b/Master/texmf-dist/doc/context/third/enigma/enigma/COPYING @@ -0,0 +1,22 @@ +Copyright 2011-2013 Philipp Gesang. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/Master/texmf-dist/doc/context/third/enigma/enigma/README b/Master/texmf-dist/doc/context/third/enigma/enigma/README new file mode 100644 index 00000000000..1da3eb07903 --- /dev/null +++ b/Master/texmf-dist/doc/context/third/enigma/enigma/README @@ -0,0 +1,29 @@ +Description of files: + +./enigma-example-context.tex usage example for Context +./enigma-example-latex.tex usage example for LaTeX +./enigma-example-plain.tex usage example for Plain +./COPYING License (BSD) +./enigma_manual.tex manual source (Context) +./enigma-doc.pdf compiled manual +./mtx-t-enigma.lua mtx script (run as “mtxrun --script enigma”) +./enigma.lua format independent code +./t-enigma.mkvi Context interface +./enigma.sty wrapper for LaTeX (inputs enigma.tex) +./enigma.tex Plain interface + +This is the directory layout I have in mind: + +enigma_manual.tex -> ./doc/context/third/enigma/enigma_manual.tex +enigma-example-plain.tex -> ./doc/context/third/enigma/examples/enigma-example-plain.tex +enigma-example-latex.tex -> ./doc/context/third/enigma/examples/enigma-example-latex.tex +enigma-example-context.tex -> ./doc/context/third/enigma/examples/enigma-example-context.tex +enigma-doc.pdf -> ./doc/context/third/enigma/enigma-doc.pdf +COPYING -> ./doc/context/third/enigma/COPYING +mtx-t-enigma.lua -> ./scripts/context/lua/third/enigma/mtx-t-enigma.lua +t-enigma.mkvi -> ./tex/context/third/enigma/t-enigma.mkvi +enigma.sty -> ./tex/latex/enigma/enigma.sty +enigma.tex -> ./tex/plain/enigma/enigma.tex +enigma.lua -> ./tex/generic/enigma/enigma.lua + + diff --git a/Master/texmf-dist/doc/context/third/enigma/enigma/enigma-doc.pdf b/Master/texmf-dist/doc/context/third/enigma/enigma/enigma-doc.pdf new file mode 100644 index 00000000000..2a9a2f9ab78 Binary files /dev/null and b/Master/texmf-dist/doc/context/third/enigma/enigma/enigma-doc.pdf differ diff --git a/Master/texmf-dist/doc/context/third/enigma/enigma/enigma_manual.tex b/Master/texmf-dist/doc/context/third/enigma/enigma/enigma_manual.tex new file mode 100644 index 00000000000..4331521900e --- /dev/null +++ b/Master/texmf-dist/doc/context/third/enigma/enigma/enigma_manual.tex @@ -0,0 +1,498 @@ +%% If you’re looking for instructions concerning how to build this +%% manual, please for now drop me an email: ·phg42.2a at gmail dot com· +%% as you will need a certain mtx-script. +\defineitemgroup [credits] +\setupitemgroup [credits] [ + % before={\startnarrower[middle]}, + % after=\stopnarrower, + left=, + right=, + margin=, + option=2, +] + +\pushcatcodetable +\setcatcodetable\txtcatcodes +\useurl [chickenize] [https://github.com/alt/chickenize] +\useurl [codebook] [http://simonsingh.net/books/the-code-book/] +\useurl [key_procedure] [http://users.telenet.be/d.rijmenants/en/enigmaproc.htm] +\useurl [luatex-web] [http://www.luatex.org/] +\useurl [rotor_wirings] [http://www.ellsbury.com/ultraenigmawirings.htm] +\useurl [wp:day_key] [http://en.wikipedia.org/wiki/Cryptanalysis_of_the_Enigma#Key_setting] + [] [\hyphenatedurl{http://en.wikipedia.org/wiki/Cryptanalysis_of_the_Enigma\#Key_setting}] +\useurl [wp:ring] [http://en.wikipedia.org/wiki/Enigma_rotor_details#The_ring_setting] + [] [\hyphenatedurl{http://en.wikipedia.org/wiki/Enigma_rotor_details\#The_ring_setting}] +\useurl [initialization] [http://w1tp.com/enigma/mewirg.htm] +\useurl [model-i] [http://www.cryptomuseum.com/crypto/enigma/i/index.htm] +\useurl [ecollection] [http://cryptocellar.org/enigma/] +\useurl [heinz_diss] [http://rzbl04.biblio.etc.tu-bs.de:8080/docportal/receive/DocPortal_document_00001705] +\popcatcodetable + +\startdocchapter[title=Introduction] + This module implements an \emph{Enigma} cipher that is equivalent to + the most widely distributed model: the Enigma~I (subtypes {\sc m1} + {\sc m2} and {\sc m3}).% + \footnote{% + See \from[model-i] for a showcase. + } + Machines of this type had three rotors, a non-moving reflector and, + as a novelty at the time of their introduction, a plugboard. + The simulation accepts arbitrary configurations of these components, + as well as the starting position of the rotors, and then processes + text accordingly. + Depending on the input, this yields the plaintext or ciphertext, as + encryption and decryption are the same. + + The code is provided as a module (interface for \CONTEXT) as well as + a package (\PLAIN, \LATEX). + It is subject to the {\sc bsd} license, see below, + \at{page}[license], for details. +\stopdocchapter + +\startdocchapter[title=Usage] + +\startdocsection[title=Loading the Module / Package] + The intention is for the \modulename{Enigma} codebase to integrate + with the three most popular (as of 2012) \TEX\ formats: + \CONTEXT, + \PLAIN, and + \LATEX. + If the user interface does not fully conform with the common practice + of the latter two, please be lenient toward the author whose + intuitions are for the most part informed by \CONTEXT. + For this reason, a couple words concerning the interfaces will be + necessary. + The examples in this manual will be cycling through all three + formats, but once you get the general idea of how it works, you will + have no problem translating between coding styles. + Those familiar with \CONTEXT\ might, therefore, skip the following + paragraphs and continue directly with the next section on + \at{page}[sec:opts].% + + The package is loaded as usual. For \PLAIN, issue a + \type{\input{enigma}}. + \LATEX-users need to place \type{\usepackage{enigma}} somewhere + inside the preamble. + (There are no package options.) + From this point on, instructions for both formats are the same. + + The interface provides two basic macros from which all functionality + will be derived: + \texmacro{defineenigma} and \texmacro{setupenigma}. + Both are a kind of \emph{meta-macros}, meaning that they generate + other macros which may then be employed to access the functionality + of the package. + As such they naturally belong inside the preamble (if you chose to + use + \modulename{Enigma} with \LATEX, that is). + The correct order is to \texmacro{defineenigma} an enigma machine + first and then \texmacro{setupenigma} it. + The definition takes a single, setups a double mandatory argument. + Thus, \type{\defineenigma{encrypt}} creates a new environment + consisting of the macros \texmacro{beginencrypt} and + \texmacro{endencrypt}.% + \footnote{% + \CONTEXT-users will have noticed that there is no direct macro + \type{\encrypt{foo}}. The reason for this is that the callback + which the module relies on operates on node-level. + This means that for the Enigma encryption to have an effect it will + have to process entire paragraphs. + As encrypted passages are supposed to stand on their own, this + small limitation should not have a severe impact on functionality. + If you should, however, need a macro that works for smaller + portions of text, please send a feature request to the maintainer + (\ichdparameter{email}). + } + These correspond to \texmacro{startencrypt}/\texmacro{stopencrypt} in + the \CONTEXT\ interface. + The \CONTEXT-examples below are easily translated to + Plain/\LATEX-syntax by replacing curly brackets (groups) with + square brackets and substituting environment prefixes: + \type{\start} becomes \type{\begin} and + \type{\stop} becomes \type{\end}. + Except for those conventions the syntax, even in key-value + assignments, is identical. + + However, the environment is not usable right away, as we still have + to set the initial state of the machine. + This is achieved by the second meta-macro, + \type{\setupenigma{encrypt}{}}, where \type{} is a + placeholder for a list of \emph{assignments}, i.\,e. pairs of + \emph{key=value} statements by means of which all further parameters + are specified. + The possible parameters are listed in the next section, + examples of their effects will be given further below in the section + on functionality (see \at{page}[sec:fun]).% + \footnote{% + If you grasp the concept of paired \type{\define} + \endash\space \type{\setup} macros, then congratulations are + in order: you qualify for migration from your current macro package + to \CONTEXT. + } +\stopdocsection +\startdocsection[title=Options Explained,reference=sec:opts] + +At the moment, the \texmacro{setupenigma} macro in any format accepts +the following parameters. +% \startpostponing[+2]%% messes up floats -- why? + \startplacefigure[ + location={right,high}, + title={Usage example for the \PLAIN\ format.}, + ] + % \framed[align=right,frameoffset=1em]{% + \startframed[ + align=right, + frameoffset=1em, + frame=off, + ]% + \startlatextyping[before=,after=,] + \input {enigma} + %% Definition ··········· %% + \defineenigma {encryption} + %% Setup ················ %% + \setupenigma {encryption} { + other_chars = no, + day_key = I II III + 01 01 01, + rotor_setting = aaa, + spacing = yes, + verbose = 1, + } + %% Usage ················ %% + \beginencryption + aaaaa aaaaa aaaaa + aaaaa aaaaa aaaaa + \endencryption + \beginencryption + Nobody in Poland is going + to be able to read this, + har har! + \endencryption + \bye + \stoplatextyping + \stopframed + \stopplacefigure +% \stoppostponing +\startitemize + \setuptolerance[tolerant]%% it’s crowded in here due to the float + \let\olditem\item + \let\item\undefined + \def\item#1#2{% 1 name, 2 type + \olditem + \identifier{#1}% + \reference[option:#1]{}% + \space + <\emph{#2}>% + \hskip1em% + } + \item{other_chars}{boolean} How to handle non-encodable characters, + i.\,e. glyphs outside the bare set of Latin letters; see below on + \at{page}[explained:other_chars]. + \item{day_key}{string} Encryption key, i.\,e. a description of the + initial setup of an Enigma machine: the reflector used, the choice + and positions of the three rotors, the ring settings, and the + plugboard wiring. + \item{rotor_setting}{string} The initial rotor advancement. + \item{spacing}{boolean} Auto-space output? + \item{verbose}{integer} Controls overall verbosity level + (\emph{global}\/!). +\stopitemize + +%% day key +\noindentation +To state the obvious, the value of \identifier{day_key} serves as the +\emph{day key} for encryption. An Enigma day key ordinarily consists of +(1) a list of the the rotor configuration, +(2) the ring settings, and +(3) the plugboard connections.\footnote{% + For a description of the initialization process see + \from[initialization]. +} +Together these have the denotation \emph{day key}, because they are +meant to be supplied in special code books by central authority, one +for each day.\footnote{% + Read about the historical directives for daily key renewal at + \from[key_procedure]. + there are some {\sc pdf}s with images of \emph{Kenngruppenbücher} at + \from[ecollection], section \emph{Enigma Messages and Keys}. + Also, while you’re at it, don’t miss the explanation on Wikipedia: + \from[wp:day_key]. +} +In the \modulename{Enigma} setup, the day key starts +with a triple of Roman numerals ({\sc i} to {\sc v}) describing which +of the five rotors is located in which of the three slots. +(e.\,g. \type{I VI II}).\footnote{% + For the individual wirings of the five rotors see + \from[rotor_wirings], as well as the implementation below at + \at{page}[listing:rotor_wiring]. +} +Its next part is the ring setting, a triple of two-digit integers that +are the amount by which the internal wiring of each rotor has been +shifted (\type{03 23 11}). As the Enigma encrypts only the letters of +the Latin alphabet, sane values range from one (first position: no +shift) to twenty six.\footnote{% + Consult \from[wp:ring] for an introduction into the ring mechanics. +} +The third section specifies which pairs of letters are substituted by +each other by means of plugboard connections (\type{NI CE GO LD ...}). +There can be zero to thirteen of these redirections, thus the presence +of this section is entirely optional. +Also part of the \identifier{day_key}, but not mentioned yet, is the +choice of the \emph{reflector}. +It may be specified as one of the three letters \type{A}, \type{B} and +\type{C} as the first item. If no reflector is requested explicitly, +the machine defaults to \type{B}, which is actually the only one of the +three models that had been in widespread use +(see below on \at{page}[listing:reflector] for the wirings). + +Initialization is not complete without a \identifier{rotor_setting}. +This is a triple of letters, each representing the initial state of one +rotor (e.\,g. \type{fkd}). Historically this was not part of the day +key but supposed to be chosen at random by the operating signal +officer. + +The output can be automatically grouped into sequences of five +characters, delimited by spaces (option \identifier{spacing}). This +does not only conform with traditional crypto-style, but also allows +for the resulting text to be sanely broken into lines by \TEX. + +%%% other_chars +Most documents don’t naturally adhere to the machine-imposed +restriction to the 26 letters of the Latin alphabet. The original +encipherment directives comprised substitution tables to compensate for +a set of intrinsic peculiarities of the German language, like umlauts +and common digraphs. The \modulename{Enigma} simulation module strives +to apply these automatically but there is no guarantee of completeness. + +However,\reference[explained:other_chars]{} the Enigma lacks means of +handling languages other than German. When the substitution lookup +fails, there are two ways of proceeding: either to ignore the current +character or to pass it on to the output as if nothing happened. The +default behaviour is to drop alien letters and move on. If the user +intends to keep these foreign characters instead, E can achieve this by +setting the \identifier{other_chars} key in the \modulename{Enigma} +setup to the value \emph{true}. An example of how the result of both +methods may look, other things being equal, is given in below listing +(example for \CONTEXT; see the file \type{enigma-example-context.tex} +in the \type{doc/} subtree of your installation path). + +\startcontexttyping +\usemodule [enigma] +\defineenigma [secretmessage] +\setupenigma [secretmessage] [ + other_chars = yes, + day_key = B V III II 12 03 01 GI JV KZ WM PU QY AD CN ET FL, + rotor_setting = ben, +] + +\defineenigma [othermessage] [secretmessage] +\setupenigma [othermessage] [other_chars=wrong] + +\starttext + +\startsecretmessage + føo bąr baž +\stopsecretmessage +\startothermessage + føo bąr baž +\stopothermessage + +\stoptext +\stopcontexttyping + +Both methods have their disadvantages: if the user chooses to have the +unknown characters removed it might distort the decrypted text to +becoming illegible. Far more serious, however, are the consequences of +keeping them. As artefacts in the ciphertext they would convey +information about the structure of the plain text. + +\stopdocsection + +\startdocsection[title=Basic Functionality,reference=sec:fun] + +Encrypt the text of your document using the script interface. For +a start try out the settings as given in below listing. + +\starttyping +mtxrun --script mtx-t-enigma \ + --setup="day_key = B I II III 01 01 01, \ + rotor_setting = xyz, \ + verbose=0" \ + --text="Gentlemen don’t read each other’s mail, Mr. Turing\!" +\stoptyping + +This will result in the thoroughly scrambled string +\typ{omribshpwfrfjovkntgqgi abbkhjpxmhdztapkatwrvf}. +Then, use the same settings you encrypted the text with in your +document. + +\startcontexttyping +\usemodule[enigma] +\defineenigma [secretmessage] +\setupenigma [secretmessage] [ + day_key = B I II III 01 01 01, + rotor_setting = xyz, + verbose=3, +] + +\starttext + +\startsecretmessage + omribshpwfrfjovkntgqgiabbkhjpxmhdztapkatwrvf +\stopsecretmessage + +\stoptext +\stopcontexttyping + +If you compile this document with \CONTEXT, the plain text will +reappear. +Notice that punctuation is substituted with the letter “x” before +encryption and that spaces are omitted. + +\useURL[kgbuch] [http://de.wikipedia.org/wiki/Kenngruppenbuch] + [] [code book] +Now it’s certainly not wise to carry around the key to encrypted +documents as plain text within those documents. The keys will have to +be distributed via an independent channel, e.\,g. a \from[kgbuch]. +Keys in general don’t have to be supplied inside the document. If there +is none specified, the module will interrupt the \TEX\ run and +\emph{ask} for user input. Suppose Alice wanted to send an encrypted +file to Bob and already generated the cipher text as follows: + +\starttyping +mtxrun --script mtx-t-enigma \ + --setup="day_key =B I IV V 22 07 10 AZ DG IE YJ QM CW, \ + rotor_setting = bar, \ + verbose=0" \ + --text="I have nothing to hide. From the NSA, that is." +\stoptyping + +Alice would then include the result of this line in her \LATEX\ +document as follows: + +\startlatextyping +\documentclass{scrartcl} +\usepackage{enigma} +\defineenigma{decryption} +%% Encryption key not given in the setup. +\setupenigma{decryption}{ + rotor_setting = bar, + verbose = 3, +} +\begin{document} + +\begindecryption +usbatbwcaajhzgeyzkqskupzbmdhbdepccgeh +\enddecryption + +\end{document} +\stoplatextyping + +She subsequently mails this file to Bob and conveys the key through a +secure channel. They only thing that will be left for Bob to do now, is +to enter the key at the prompt when compiling the document with +\LUALATEX. +\stopdocsection + +\startdocsection[title=Uses and Abuses] + +In \LUATEX, \identifier{callback}s may stack. This allows filtering the +input through many enigma machines successively. For instance, in the +following listing, two instances of the same machine are generated and +applied. + +\startcontexttyping +\usemodule [enigma] %% load the module +\defineenigma [secretmessage] %% generate and +\setupenigma [secretmessage] [ %% configure a machine + day_key = B IV V II 01 01 01 AD CN ET FL GI JV KZ PU QY WX, + rotor_setting = foo, + verbose=3, +] + +%% now, copy the first machine’s settings +\defineenigma [othermessage] [secretmessage] + +%% here we go! +\starttext + +\startothermessage %% enable machine 1 +\startsecretmessage %% enable machine 2 while no 1 is active +Encryption equals decryption. +\stopothermessage +\stopsecretmessage + +\stoptext \endinput +\stopcontexttyping + +\stopdocsection + +\stopdocchapter + +\startdocchapter[title=Metadata] +\startdocsection[title=License,reference=license] + +© 2012--2013 \emph{Philipp Gesang}. All rights reserved. + +Redistribution and use in source and binary forms, with or +without modification, are permitted provided that the following +conditions are met: + +\startitemize[n] + \item Redistributions of source code must retain the above + copyright notice, this list of conditions and the following + disclaimer. + \item Redistributions in binary form must reproduce the + above copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. +\stopitemize + +\begingroup +\setuptolerance [horizontal,strict] +\startalignment [right,nothyphenated] +\noindentation\startsmallcaps + this software is provided by the copyright + holder “as is” and any express or implied warranties, + including, but not limited to, the implied warranties of + merchantability and fitness for a particular purpose are + disclaimed. in no event shall the copyright holder or + contributors be liable for any direct, indirect, incidental, + special, exemplary, or consequential damages (including, but + not limited to, procurement of substitute goods or services; + loss of use, data, or profits; or business interruption) + however caused and on any theory of liability, whether in + contract, strict liability, or tort (including negligence or + otherwise) arising in any way out of the use of this software, + even if advised of the possibility of such damage. +\stopsmallcaps\endgraf +\stopalignment +\endgroup +\stopdocsection + +\startdocsection[title=Acknowledgements] +\startcredits + \item The idea to implement the \emph{Enigma} cipher for \TEX\ came + up while I was reading \emph{The Code Book} by Simon Singh. + This work contains an excellent portrayal of the history of + German military cryptography and Allied cryptanalysis before + and during the Second World War.\par + \from[codebook] + \item A more detailed account from a historical-cryptological + perspective is provided by Ulrich Heinz in his Dissertation (in + German), which is freely available. + Includes an interesting albeit speculative note on the + effectiveness of the Soviet code breaking efforts (chapter + seven).\par + \from[heinz_diss] + \item Also, the \modulename{Enigma} module drew lots of inspiration + from Arno Trautmann’s \modulename{Chickenize} package, which + remains the unsurpassed hands-on introduction to callback + trickery.\par + \from[chickenize] + \item Finally, without \LUATEX, encryption on node-level would not + have been possible.\par + \from[luatex-web] +\stopcredits +\stopdocsection +\stopdocchapter diff --git a/Master/texmf-dist/doc/context/third/enigma/enigma/examples/enigma-example-context.tex b/Master/texmf-dist/doc/context/third/enigma/enigma/examples/enigma-example-context.tex new file mode 100644 index 00000000000..051e719697f --- /dev/null +++ b/Master/texmf-dist/doc/context/third/enigma/enigma/examples/enigma-example-context.tex @@ -0,0 +1,59 @@ +\usemodule [enigma] +%\usetypescript [computer-modern-unicode] +%\setupbodyfont [computer-modern-unicode,20pt] +%\usetypescript [iwona] +%\setupbodyfont [iwona,20pt] +\usetypescript [modern] +\setupbodyfont [modern,20pt] + +\defineenigma [nilsettings] +\setupenigma [nilsettings] [ %% a machine with vanilla settings + other_chars = no, + day_key = I II III 01 01 01, + rotor_setting = aaa, + spacing = yes, + verbose = 3, +] + +\defineenigma [othernilsettings] [nilsettings] + +\defineenigma [secretmessage] +\setupenigma [secretmessage] [ + other_chars = yes, + day_key = B V III II 12 03 01 GI JV KZ WM PU QY AD CN ET FL, + rotor_setting = ben, +] + +\defineenigma [othersecretmessage] [secretmessage] + +\defineenigma [othermessage] [secretmessage] +\setupenigma [othermessage] [other_chars=wrong] + +\starttext + +\placefigure{Enigma output and its reversal using nil settings}{ + \framed [width=.7\hsize,frame=off,align=middle]{ + \startnilsettings + aaaaa aaaaa aaaaa aaaaa aaaaa aaaaa aaaaa aaaaa aa + \stopnilsettings + + \startothernilsettings + bdzgo wcxlt ksbtm cdlpb muqof xyhcx tgyjf linhn xs + \stopothernilsettings + } +} + +\startsecretmessage + føo bąr baž +\stopsecretmessage + +\startothersecretmessage + iøtyąwqiž +\stopothersecretmessage + +\startothermessage + føo bąr baž +\stopothermessage + +\stoptext +% vim:ft=context:tw=71:ts=2:sw=2:expandtab diff --git a/Master/texmf-dist/doc/context/third/enigma/enigma/examples/enigma-example-latex.tex b/Master/texmf-dist/doc/context/third/enigma/enigma/examples/enigma-example-latex.tex new file mode 100644 index 00000000000..ea825cf1e9e --- /dev/null +++ b/Master/texmf-dist/doc/context/third/enigma/enigma/examples/enigma-example-latex.tex @@ -0,0 +1,53 @@ +\documentclass[a5paper]{scrartcl} +\parindent0pt +%%····································································% +\usepackage {enigma} +%%····································································% +%% The first machine will be used for encryption of our plain text. +\defineenigma {encryption} +\setupenigma {encryption} { + other_chars = yes, + day_key = B V III II 12 03 01 GI JV KZ WM PU QY AD CN ET FL, + rotor_setting = ben, + spacing = yes, + verbose = 2, +} +%%····································································% +%% This second machine below will be used to decrypt the string. It is +%% initialized with exactly the same settings as the first one. The +%% reason for this is that we can’t reuse the “encryption” machine as +%% it will already have progressed to some later state after the +%% encryption. Applying it on the ciphertext would yield a totally +%% different string. Hence the need for another machine. +\defineenigma{decryption} +\setupenigma{decryption}{ + other_chars = yes, + day_key = B V III II 12 03 01 GI JV KZ WM PU QY AD CN ET FL, + rotor_setting = ben, +} +%%····································································% +\begin{document} + +%%····································································% +%% Ciphertext in the PDF. Rely on the addressee to decrypt the document +%% externally. +\beginencryption + Never underestimate the amount of money, time, and effort someone + will expend to thwart a security system. +\endencryption + +%%····································································% +%% Input string generated with: +%% mtxrun --script t-enigma \ +%% --setup="day_key=B V III II 12 03 01 GI JV KZ WM PU QY AD CN ET FL,\ +%% rotor_setting=ben,\ +%% other_chars=yes"\ +%% --text="Never underestimate the amount of money,\ +%% time, and effort someone will expend to\ +%% thwart a security system." +\begindecryption + aqsnwyxgqarpuzrdktscbslaqmdhyonywxkwhcdgphvuqsspfjwhlszahygjbzmfpcpbniahvfcuradntepxsfchnn +\enddecryption +%%····································································% +\end{document} +% vim:ft=tex:tw=71:expandtab:ts=2:sw=2 diff --git a/Master/texmf-dist/doc/context/third/enigma/enigma/examples/enigma-example-plain.tex b/Master/texmf-dist/doc/context/third/enigma/enigma/examples/enigma-example-plain.tex new file mode 100644 index 00000000000..bd50c93ca4a --- /dev/null +++ b/Master/texmf-dist/doc/context/third/enigma/enigma/examples/enigma-example-plain.tex @@ -0,0 +1,50 @@ +\parindent0pt +%%····································································% +\input {enigma} +%%····································································% +%% The first machine will be used for encryption of our plain text. +\defineenigma {encryption} +\setupenigma {encryption} { + other_chars = yes, + day_key = B V III II 12 03 01 GI JV KZ WM PU QY AD CN ET FL, + rotor_setting = ben, + spacing = yes, + verbose = 1, +} +%%····································································% +%% This second machine below will be used to decrypt the string. +\defineenigma{decryption} +\setupenigma{decryption}{ + other_chars = yes, + day_key = B V III II 12 03 01 GI JV KZ WM PU QY AD CN ET FL, + rotor_setting = ben, +} +%%····································································% +%% main +%%····································································% +%% Ciphertext in the PDF. Rely on the addressee to decrypt the document +%% externally. +%%····································································% +%% encryption of plain text +%%····································································% +\beginencryption + Never underestimate the amount of money, time, and effort someone + will expend to thwart a security system. +\endencryption + +%%····································································% +%% decryption of ciphertext +%%····································································% +%% Input string generated with: +%% mtxrun --script t-enigma \ +%% --setup="day_key=B V III II 12 03 01 GI JV KZ WM PU QY AD CN ET FL,\ +%% rotor_setting=ben,\ +%% other_chars=yes"\ +%% --text="Never underestimate the amount of money,\ +%% time, and effort someone will expend to\ +%% thwart a security system." +\begindecryption + aqsnwyxgqarpuzrdktscbslaqmdhyonywxkwhcdgphvuqsspfjwhlszahygjbzmfpcpbniahvfcuradntepxsfchnn +\enddecryption +%%·····································································% +\bye diff --git a/Master/texmf-dist/scripts/context/lua/third/enigma/mtx-t-enigma.lua/mtx-t-enigma.lua b/Master/texmf-dist/scripts/context/lua/third/enigma/mtx-t-enigma.lua/mtx-t-enigma.lua new file mode 100644 index 00000000000..1b093afb759 --- /dev/null +++ b/Master/texmf-dist/scripts/context/lua/third/enigma/mtx-t-enigma.lua/mtx-t-enigma.lua @@ -0,0 +1,72 @@ +-- +-------------------------------------------------------------------------------- +-- FILE: mtx-t-enigma.lua +-- USAGE: mtxrun --script enigma --setup="s" --text="t" +-- DESCRIPTION: context script interface for the Enigma module +-- REQUIREMENTS: latest ConTeXt MkIV +-- AUTHOR: Philipp Gesang (Phg), +-- CREATED: 2013-03-28 02:14:05+0100 +-------------------------------------------------------------------------------- +-- + +environment.loadluafile("enigma") + +local iowrite = io.write + +local helpinfo = [[ +=============================================================== + The Enigma module, command line interface. + © 2012--2013 Philipp Gesang. License: 2-clause BSD. + Home: +=============================================================== + +USAGE: + + mtxrun --script enigma --setup="settings" --text="text" + --verbose=int + + where the settings are to be specified as a comma-delimited + conjunction of “key=value” statements, and “text” refers to + the text to be encoded. Note that due to the involutory + design of the enigma cipher, the text can be both plaintext + and ciphertext. + +=============================================================== +]] + +local application = logs.application { + name = "mtx-t-enigma", + banner = "The Enigma for ConTeXt, hg-rev 37+", + helpinfo = helpinfo, +} + +local ea = environment.argument + +local setup, text = ea"setup" or ea"s", ea"text" or ea"t" +local verbose = ea"verbose" or ea"v" + +local out = function (str) + iowrite(str) +end + +local machine_id = "external" +if setup and text then + local args = enigma.parse_args(setup) + if not args then + application.help() + iowrite"\n\n[Error] Could not process enigma setup!\n\n" + end + enigma.save_raw_args(args, machine_id) + --local machine = enigma.new_machine(enigma.parse_args(setup)) + local machine = enigma.new_machine(machine_id) + --machine.name = machine_id + local result = machine:encode_string(text) + if result then + out(result) + else + application.help() + end +else + application.help() +end + diff --git a/Master/texmf-dist/tex/context/third/enigma/t-enigma.mkv/t-enigma.mkvi b/Master/texmf-dist/tex/context/third/enigma/t-enigma.mkv/t-enigma.mkvi new file mode 100644 index 00000000000..b5fea293a88 --- /dev/null +++ b/Master/texmf-dist/tex/context/third/enigma/t-enigma.mkv/t-enigma.mkvi @@ -0,0 +1,157 @@ +%D \module +%D [ file=t-enigma.mkvi, +%D version=2013-03-28 02:15:37+0100, +%D title=\CONTEXT\ User Module, +%D subtitle=Enigma, +%D author=Philipp Gesang, +%D date=\currentdate, +%D copyright=Philipp Gesang, +%D license=2-clause BSD, +%D email={phg42 dot 2a at gmail dot com}] +%C This module is licensed under the conditions of the BSD license with +%C two clauses. There is a copy in a file named "COPYING" in the +%C t-enigma source tree. + +\unprotect + +\writestatus{loading} {ConTeXt module / Enigma Document Encryption} + +\startinterface all + \setinterfacevariable {enigma} {enigma} +\stopinterface + +\definenamespace [\v!enigma] [ + \v!command=\v!no, + comment=Enigma Document Encryption, + \s!name=\v!enigma, + \s!parent=\v!enigma, + % setup=\v!list, + setup=\v!no, + style=\v!no, + type=module, + version=hg-tip, +] + +%D Loading the \LUA\ conversion routines. + +\startluacode +thirddata = thirddata or { } +\stopluacode +\registerctxluafile{enigma} + +%D \startdocsection[title=Macro Generator] +%D \startparagraph +%D The main setup.\reference[listing:define]{} The +%D \texmacro{defineenigma} macro does not adhere to the reommended +%D practis of automatical macro derivation. Rather, we have our own +%D parser do the job of setting globals. This is a consequence of the +%D intention to offer the same behavior in any of the three main +%D formats, \PLAIN, \CONTEXT and \LATEX. Hence, we don’t rely on the +%D internal mechanisms but implement our own macro generator. +%D \stopparagraph + +\def\enigma_define_indeed#id{% + \edef\enigmaid{#id}% + \expandafter\gdef\csname \e!start\enigmaid\endcsname{% + \endgraf + \bgroup + \ctxlua{ + if thirddata.enigma.machines["#id"] then + nodes.tasks.enableaction("processors", + "thirddata.enigma.callbacks.#id") + else + print([[ENIGMA: No machine of that name: #id!]]) + end + }% + }% + % + \expandafter\gdef\csname \e!stop\enigmaid\endcsname{% + \endgraf%% We need to force a paragraph here for the callback to be + %% applied. + \ctxlua{ + nodes.tasks.disableaction("processors", + "thirddata.enigma.callbacks.#id") + thirddata.enigma.machines["#id"]:processed_chars() + }% + \egroup% + }% +} + +%D \startparagraph +%D The \texmacro{enigma_inherit}\reference[listing:inherit]{} is called +%D as an intermediate step when deriving one machine from an already +%D existing one. It gets the stored configuration of its ancestor, +%D relying on the \luafunction{retrieve_raw_args} function (see +%D \at{page}[listing:retrieve]. +%D \stopparagraph +\def\enigma_inherit#to#from{% + \ctxlua{% + local enigma = thirddata.enigma + local current_args = enigma.retrieve_raw_args(\!!bs#from\!!es) + enigma.save_raw_args(current_args, \!!bs#to\!!es) + enigma.new_callback(enigma.new_machine(\!!bs#to\!!es), + \!!bs#to\!!es) + }% + \enigma_define_indeed{#to}% +} + +\def\enigma_define[#id][#secondid]{% + \ifsecondargument %% Copy an existing machine and callback. + \enigma_inherit{#id}{#secondid}% + \else %% Create a new machine. + \iffirstargument + \enigma_define_indeed{#id}% + \else + \donothing + \fi + \fi + \endgroup% +} + +\def\defineenigma{% + \begingroup + \dodoubleempty\enigma_define +} + +%D \stopdocsection + +%D \startdocsection[title=Setup] +\def\enigma_setup_indeed#args{% + \ctxlua{ + local enigma = thirddata.enigma + local current_args = + enigma.parse_args(\!!bs\detokenize{#args}\!!es) + enigma.save_raw_args(current_args, \!!bs\currentenigmaid\!!es) + enigma.new_callback( + enigma.new_machine(\!!bs\currentenigmaid\!!es), + \!!bs\currentenigmaid\!!es) + }% +} + +%D \startparagraph +%D The module setup \texmacro{setupenigma} expects key=value, notation. +%D All the logic is at the Lua end, not much to see here … +%D \stopparagraph + +\def\enigma_setup[#id][#args]{% + \ifsecondargument + \edef\currentenigmaid{#id} + \pushcatcodetable + \catcodetable \txtcatcodes + \enigma_setup_indeed{#args}% + \else + \donothing + \fi + \popcatcodetable + \egroup% +} + +\def\setupenigma{% + \bgroup + \dodoubleempty\enigma_setup% +} + +%D \stopdocsection + +\protect +% vim:ft=context:sw=2:ts=2:tw=71 diff --git a/Master/texmf-dist/tex/generic/enigma/enigma.lua b/Master/texmf-dist/tex/generic/enigma/enigma.lua new file mode 100644 index 00000000000..9da428842bd --- /dev/null +++ b/Master/texmf-dist/tex/generic/enigma/enigma.lua @@ -0,0 +1,1662 @@ +#!/usr/bin/env texlua +----------------------------------------------------------------------- +-- FILE: enigma.lua +-- USAGE: Call via interface from within a TeX session. +-- DESCRIPTION: Enigma logic. +-- REQUIREMENTS: LuaTeX capable format (Luaplain, ConTeXt). +-- AUTHOR: Philipp Gesang (Phg), +-- VERSION: release +-- CREATED: 2013-03-28 02:12:03+0100 +----------------------------------------------------------------------- +-- + +--[[ichd-- +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\startdocsection[title=Format Dependent Code] +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\startparagraph +Exported functionality will be collected in the table +\identifier{enigma}. +\stopparagraph +--ichd]]-- + +local enigma = { machines = { }, callbacks = { } } +local format_is_context = false + +--[[ichd-- +\startparagraph +Afaict, \LATEX\ for \LUATEX\ still lacks a globally accepted +namespacing convention. This is more than bad, but we’ll have to cope +with that. For this reason we brazenly introduce +\identifier{packagedata} (in analogy to \CONTEXT’s +\identifier{thirddata}) table as a package namespace proposal. If this +module is called from a \LATEX\ or plain session, the table +\identifier{packagedata} will already have been created so we will +identify the format according to its presence or absence, respectively. +\stopparagraph +--ichd]]-- + +if packagedata then -- latex or plain + packagedata.enigma = enigma +elseif thirddata then -- context + format_is_context = true + thirddata.enigma = enigma +else -- external call, mtx-script or whatever + _ENV.enigma = enigma +end +--[[ichd-- +\stopdocsection +--ichd]]-- + +--[[ichd-- +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\startdocsection[title=Prerequisites] +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\startparagraph +First of all, we generate local copies of all those library functions +that are expected to be referenced frequently. +The format-independent stuff comes first; it consists of functions from +the +\identifier{io}, +\identifier{lpeg}, +\identifier{math}, +\identifier{string}, +\identifier{table}, and +\identifier{unicode} +libraries. +\stopparagraph +--ichd]]-- + +local get_debug_info = debug.getinfo +local ioread = io.read +local iowrite = io.write +local mathfloor = math.floor +local mathrandom = math.random +local next = next +local nodecopy = node and node.copy +local nodeid = node and node.id +local nodeinsert_before = node and node.insert_before +local nodeinsert_after = node and node.insert_after +local nodelength = node and node.length +local nodenew = node and node.new +local noderemove = node and node.remove +local nodeslide = node and node.slide +local nodetraverse = node and node.traverse +local nodetraverse_id = node and node.traverse_id +local nodesinstallattributehandler +local nodestasksappendaction +local nodestasksdisableaction +if format_is_context then + nodesinstallattributehandler = nodes.installattributehandler + nodestasksappendaction = nodes.tasks.appendaction + nodestasksdisableaction = nodes.tasks.disableaction +end +local stringfind = string.find +local stringformat = string.format +local stringlower = string.lower +local stringsub = string.sub +local stringupper = string.upper +local tableconcat = table.concat +local tonumber = tonumber +local type = type +local utf8byte = unicode.utf8.byte +local utf8char = unicode.utf8.char +local utf8len = unicode.utf8.len +local utf8lower = unicode.utf8.lower +local utf8sub = unicode.utf8.sub +local utfcharacters = string.utfcharacters + +--- debugging tool (careful, this *will* break context!) +--dofile(kpse.find_file("lualibs-table.lua")) -- archaic version :( +--table.print = function (...) print(table.serialize(...)) end + +local tablecopy +if format_is_context then + tablecopy = table.copy +else -- could use lualibs instead but not worth the overhead + tablecopy = function (t) -- ignores tables as keys + local result = { } + for k, v in next, t do + if type(v) == table then + result[k] = tablecopy(v) + else + result[k] = v + end + end + return result + end +end + +local GLYPH_NODE = node and nodeid"glyph" +local GLUE_NODE = node and nodeid"glue" +local GLUE_SPEC_NODE = node and nodeid"glue_spec" +local KERN_NODE = node and nodeid"kern" +local DISC_NODE = node and nodeid"disc" +local HLIST_NODE = node and nodeid"hlist" +local VLIST_NODE = node and nodeid"vlist" + +local IGNORE_NODES = node and { +--[GLUE_NODE] = true, + [KERN_NODE] = true, +--[DISC_NODE] = true, +} or { } + +--[[ichd-- +\startparagraph +The initialization of the module relies heavily on parsers generated by +\type{LPEG}. +\stopparagraph +--ichd]]-- + +local lpeg = require "lpeg" + +local C, Cb, Cc, Cf, Cg, + Cmt, Cp, Cs, Ct + = lpeg.C, lpeg.Cb, lpeg.Cc, lpeg.Cf, lpeg.Cg, + lpeg.Cmt, lpeg.Cp, lpeg.Cs, lpeg.Ct + +local P, R, S, V, lpegmatch + = lpeg.P, lpeg.R, lpeg.S, lpeg.V, lpeg.match + +--local B = lpeg.version() == "0.10" and lpeg.B or nil + +--[[ichd-- +\startparagraph +By default the output to \type{stdout} will be zero. The verbosity +level can be adjusted in order to alleviate debugging. +\stopparagraph +--ichd]]-- +--local verbose_level = 42 +local verbose_level = 0 + +--[[ichd-- +\startparagraph +Historically, Enigma-encoded messages were restricted to a size of 250 +characters. With sufficient verbosity we will indicate whether this +limit has been exceeded during the \TEX\ run. +\stopparagraph +--ichd]]-- +local max_msg_length = 250 +--[[ichd-- +\stopdocsection +--ichd]]-- + + +--[[ichd-- +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\startdocsection[title=Globals] +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\startparagraph +The following mappings are used all over the place as we convert back +and forth between the characters (unicode) and their numerical +representation. +\stopparagraph +--ichd]]-- + +local value_to_letter -- { [int] -> chr } +local letter_to_value -- { [chr] -> int } +local alpha_sorted -- string, length 26 +local raw_rotor_wiring -- { string0, .. string5, } +local notches -- { [int] -> int } // rotor num -> notch pos +local reflector_wiring -- { { [int] -> int }, ... } // symmetrical +do + value_to_letter = { + "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", + "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", + } + + letter_to_value = { + a = 01, b = 02, c = 03, d = 04, e = 05, f = 06, g = 07, h = 08, + i = 09, j = 10, k = 11, l = 12, m = 13, n = 14, o = 15, p = 16, + q = 17, r = 18, s = 19, t = 20, u = 21, v = 22, w = 23, x = 24, + y = 25, z = 26, + } +--[[ichd-- +\startparagraph +The five rotors to simulate.\reference[listing:rotor_wiring]{} +Their wirings are created from strings at runtime, see below the +function \luafunction{get_rotors}. +\stopparagraph +--ichd]]-- + + --[[ + Nice: http://www.ellsbury.com/ultraenigmawirings.htm + ]]-- + alpha_sorted = "abcdefghijklmnopqrstuvwxyz" + raw_rotor_wiring = { + [0] = alpha_sorted, + "ekmflgdqvzntowyhxuspaibrcj", + "ajdksiruxblhwtmcqgznpyfvoe", + "bdfhjlcprtxvznyeiwgakmusqo", + "esovpzjayquirhxlnftgkdcmwb", + "vzbrgityupsdnhlxawmjqofeck", + } + +--[[ichd-- +\startparagraph +Notches are assigned to rotors according to the Royal Army +mnemonic. +\stopparagraph +--ichd]]-- + notches = { } + do + local raw_notches = "rfwkannnn" + --local raw_notches = "qevjz" + local n = 1 + for chr in utfcharacters(raw_notches) do + local pos = stringfind(alpha_sorted, chr) + notches[n] = pos - 1 + n = n + 1 + end + end + +--[[ichd-- +\placetable[here][listing:reflector]% + {The three reflectors and their substitution rules.}{% + \starttabulate[|r|l|] + \NC UKW a \NC AE BJ CM DZ FL GY HX IV KW NR OQ PU ST \NC \NR + \NC UKW b \NC AY BR CU DH EQ FS GL IP JX KN MO TZ VW \NC \NR + \NC UKW c \NC AF BV CP DJ EI GO HY KR LZ MX NW QT SU \NC \NR + \stoptabulate +} +--ichd]]-- + + reflector_wiring = { } + local raw_ukw = { + { a = "e", b = "j", c = "m", d = "z", f = "l", g = "y", h = "x", + i = "v", k = "w", n = "r", o = "q", p = "u", s = "t", }, + { a = "y", b = "r", c = "u", d = "h", e = "q", f = "s", g = "l", + i = "p", j = "x", k = "n", m = "o", t = "z", v = "w", }, + { a = "f", b = "v", c = "p", d = "j", e = "i", g = "o", h = "y", + k = "r", l = "z", m = "x", n = "w", q = "t", s = "u", }, + } + for i=1, #raw_ukw do + local new_wiring = { } + local current_ukw = raw_ukw[i] + for from, to in next, current_ukw do + from = letter_to_value[from] + to = letter_to_value[to] + new_wiring[from] = to + new_wiring[to] = from + end + reflector_wiring[i] = new_wiring + end +end + +--[[ichd-- +\stopdocsection +--ichd]]-- + +--[[ichd-- +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\startdocsection[title=Pretty printing for debug purposes] +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\startparagraph +The functions below allow for formatting of the terminal output; they +have no effect on the workings of the enigma simulator. +\stopparagraph +--ichd]]-- + +local emit +local pprint_ciphertext +local pprint_encoding +local pprint_encoding_scheme +local pprint_init +local pprint_machine_step +local pprint_new_machine +local pprint_rotor +local pprint_rotor_scheme +local pprint_step +local polite_key_request +local key_invalid +do + local eol = "\n" + + local colorstring_template = "\027[%d;1m%s\027[0m" + local colorize = function (s, color) + color = color and color < 38 and color > 29 and color or 31 + return stringformat(colorstring_template, + color, + s) + end + + local underline = function (s) + return stringformat("\027[4;37m%s\027[0m", s) + end + + local s_steps = [[Total characters encoded with machine “]] + local f_warnsteps = [[ (%d over permitted maximum)]] + pprint_machine_step = function (n, name) + local sn + name = colorize(name, 36) + if n > max_msg_length then + sn = colorize(n, 31) .. stringformat(f_warnsteps, + n - max_msg_length) + else + sn = colorize(n, 37) + end + return s_steps .. name .. "”: " .. sn .. "." + end + local rotorstate = "[s \027[1;37m%s\027[0m n\027[1;37m%2d\027[0m]> " + pprint_rotor = function (rotor) + local visible = rotor.state % 26 + 1 + local w, n = rotor.wiring, (rotor.notch - visible) % 26 + 1 + local tmp = { } + for i=1, 26 do + local which = (i + rotor.state - 1) % 26 + 1 + local chr = value_to_letter[rotor.wiring.from[which]] + if i == n then -- highlight positions of notches + tmp[i] = colorize(stringupper(chr), 32) + --elseif chr == value_to_letter[visible] then + ---- highlight the character in window + -- tmp[i] = colorize(chr, 33) + else + tmp[i] = chr + end + end + return stringformat(rotorstate, + stringupper(value_to_letter[visible]), + n) + .. tableconcat(tmp) + end + + local rotor_scheme = underline"[rot not]" + .. " " + .. underline(alpha_sorted) + pprint_rotor_scheme = function () + return rotor_scheme + end + + local s_encoding_scheme = eol + .. [[in > 1 => 2 => 3 > UKW > 3 => 2 => 1]] + pprint_encoding_scheme = function () + return underline(s_encoding_scheme) + end + local s_step = " => " + local stepcolor = 36 + local finalcolor = 32 + pprint_encoding = function (steps) + local nsteps, result = #steps, { } + for i=0, nsteps-1 do + result[i+1] = colorize(value_to_letter[steps[i]], stepcolor) + .. s_step + end + result[nsteps+1] = colorize(value_to_letter[steps[nsteps]], + finalcolor) + return tableconcat(result) + end + + local init_announcement + = colorize("\n" .. [[Initial position of rotors: ]], + 37) + pprint_init = function (init) + local result = "" + result = value_to_letter[init[1]] .. " " + .. value_to_letter[init[2]] .. " " + .. value_to_letter[init[3]] + return init_announcement .. colorize(stringupper(result), 34) + end + + local machine_announcement = + [[Enigma machine initialized with the following settings.]] .. eol + local s_ukw = colorize(" Reflector:", 37) + local s_pb = colorize("Plugboard setting:", 37) + local s_ring = colorize(" Ring positions:", 37) + local empty_plugboard = colorize(" ——", 34) + pprint_new_machine = function (m) + local result = { "" } + result[#result+1] = underline(machine_announcement) + result[#result+1] = s_ukw + .. " " + .. colorize( + stringupper(value_to_letter[m.reflector]), + 34 + ) + local rings = "" + for i=1, 3 do + local this = m.ring[i] + rings = rings + .. " " + .. colorize(stringupper(value_to_letter[this + 1]), 34) + end + result[#result+1] = s_ring .. rings + if m.__raw.plugboard then + local tpb, pb = m.__raw.plugboard, "" + for i=1, #tpb do + pb = pb .. " " .. colorize(tpb[i], 34) + end + result[#result+1] = s_pb .. pb + else + result[#result+1] = s_pb .. empty_plugboard + end + result[#result+1] = "" + result[#result+1] = pprint_rotor_scheme() + for i=1, 3 do + result[#result+1] = pprint_rotor(m.rotors[i]) + end + return tableconcat(result, eol) .. eol + end + + local step_template = colorize([[Step № ]], 37) + local chr_template = colorize([[ —— Input ]], 37) + local pbchr_template = colorize([[ → ]], 37) + pprint_step = function (n, chr, pb_chr) + return eol + .. step_template + .. colorize(n, 34) + .. chr_template + .. colorize(stringupper(value_to_letter[chr]), 34) + .. pbchr_template + .. colorize(stringupper(value_to_letter[pb_chr]), 34) + .. eol + end + + -- Split the strings into lines, group them in bunches of five etc. + local tw = 30 + local pprint_textblock = function (s) + local len = utf8len(s) + local position = 1 -- position in string + local nline = 5 -- width of current line + local out = utf8sub(s, position, position+4) + repeat + position = position + 5 + nline = nline + 6 + if nline > tw then + out = out .. eol .. utf8sub(s, position, position+4) + nline = 1 + else + out = out .. " " .. utf8sub(s, position, position+4) + end + until position > len + return out + end + + local intext = colorize([[Input text:]], 37) + local outtext = colorize([[Output text:]], 37) + pprint_ciphertext = function (input, output, upper_p) + if upper_p then + input = stringupper(input) + output = stringupper(output) + end + return eol + .. intext + .. eol + .. pprint_textblock(input) + .. eol .. eol + .. outtext + .. eol + .. pprint_textblock(output) + end + +--[[ichd-- +\startparagraph +\luafunction{emit} is the main wrapper function for +\identifier{stdout}. Checks if the global verbosity setting exceeds +the specified threshold, and only then pushes the output. +\stopparagraph +--ichd]]-- + emit = function (v, f, ...) + if f and v and verbose_level >= v then + iowrite(f(...) .. eol) + end + return 0 + end +--[[ichd-- +\startparagraph +The \luafunction{polite_key_request} will be called in case the +\identifier{day_key} field of the machine setup is empty at the time of +initialization. +\stopparagraph +--ichd]]-- + local s_request = "\n\n " + .. underline"This is an encrypted document." .. [[ + + + Please enter the document key for enigma machine + “%s”. + + Key Format: + +Ref R1 R2 R3 I1 I2 I3 [P1 ..] Ref: reflector A/B/C + Rn: rotor, I through V + In: ring position, 01 through 26 + Pn: optional plugboard wiring, upto 32 + +>]] + polite_key_request = function (name) + return stringformat(s_request, colorize(name, 33)) + end + + local s_invalid_key = colorize"Warning!" + .. " The specified key is invalid." + key_invalid = function () + return s_invalid_key + end +end + +--[[ichd-- +\startparagraph +The functions \luafunction{new} and \luafunction{ask_for_day_key} are +used outside their scope, so we declare them beforehand. +\stopparagraph +--ichd]]-- +local new +local ask_for_day_key +do +--[[ichd-- +\stopdocsection +--ichd]]-- + +--[[ichd-- +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\startdocsection[title=Rotation] +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\startparagraph +The following function \luafunction{do_rotate} increments the +rotational state of a single rotor. There are two tests for notches: +\startitemize[n] + \item whether it’s at the current character, and + \item whether it’s at the next character. +\stopitemize +The latter is an essential prerequisite for double-stepping. +\stopparagraph +--ichd]]-- + local do_rotate = function (rotor) + rotor.state = rotor.state % 26 + 1 + return rotor, + rotor.state == rotor.notch, + rotor.state + 1 == rotor.notch + end + +--[[ichd-- +\startparagraph +The \luafunction{rotate} function takes care of rotor (\emph{Walze}) +movement. This entails incrementing the next rotor whenever the notch +has been reached and covers the corner case \emph{double stepping}. +\stopparagraph +--ichd]]-- + local rotate = function (machine) + local rotors = machine.rotors + local rc, rb, ra = rotors[1], rotors[2], rotors[3] + + ra, nxt = do_rotate(ra) + if nxt or machine.double_step then + rb, nxt, nxxt = do_rotate(rb) + if nxt then + rc = do_rotate(rc) + end + if nxxt then + --- weird: home.comcast.net/~dhhamer/downloads/rotors1.pdf + machine.double_step = true + else + machine.double_step = false + end + end + machine.rotors = { rc, rb, ra } + end +--[[ichd-- +\stopdocsection +--ichd]]-- + +--[[ichd-- +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\startdocsection[title=Input Preprocessing] +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\startparagraph +Internally, we will use lowercase strings as they are a lot more +readable than uppercase. Lowercasing happens prior to any further +dealings with input. After the encoding or decoding has been +accomplished, there will be an optional (re-)uppercasing. +\stopparagraph + +\startparagraph +Substitutions \reference[listing:preproc]{}are applied onto the +lowercased input. You might want to avoid some of these, above all the +rules for numbers, because they translate single digits only. The +solution is to write out numbers above ten. +\stopparagraph +--ichd]]-- + + local pp_substitutions = { + -- Umlauts are resolved. + ["ö"] = "oe", + ["ä"] = "ae", + ["ü"] = "ue", + ["ß"] = "ss", + -- WTF? + ["ch"] = "q", + ["ck"] = "q", + -- Punctuation -> “x” + [","] = "x", + ["."] = "x", + [";"] = "x", + [":"] = "x", + ["/"] = "x", + ["’"] = "x", + ["‘"] = "x", + ["„"] = "x", + ["“"] = "x", + ["“"] = "x", + ["-"] = "x", + ["–"] = "x", + ["—"] = "x", + ["!"] = "x", + ["?"] = "x", + ["‽"] = "x", + ["("] = "x", + [")"] = "x", + ["["] = "x", + ["]"] = "x", + ["<"] = "x", + [">"] = "x", + -- Spaces are omitted. + [" "] = "", + ["\n"] = "", + ["\t"] = "", + ["\v"] = "", + ["\\"] = "", + -- Numbers are resolved. + ["0"] = "null", + ["1"] = "eins", + ["2"] = "zwei", + ["3"] = "drei", + ["4"] = "vier", + ["5"] = "fuenf", + ["6"] = "sechs", + ["7"] = "sieben", + ["8"] = "acht", + ["9"] = "neun", + } + +--[[ichd-- +\stopdocsection +--ichd]]-- + +--[[ichd-- +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\startdocsection[ + title={Main function chain to be applied to single characters}, +] +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\startparagraph +As far as the Enigma is concerned, there is no difference between +encoding and decoding. Thus, we need only one function +(\luafunction{encode_char}) to achieve the complete functionality. +However, within every encoding step, characters will be wired +differently in at least one of the rotors according to its rotational +state. Rotation is simulated by adding the \identifier{state} field of +each rotor to the letter value (its position on the ingoing end). +\stopparagraph +\placetable[here][table:dirs]{Directional terminology}{% + \starttabulate[|l|r|l|] + \NC boolean \NC direction \NC meaning \NC \AR + \NC true \NC “from” \NC right to left \NC \AR + \NC false \NC “to” \NC left to right \NC \AR + \stoptabulate% +} +\startparagraph +The function \luafunction{do_do_encode_char} returns the character +substitution for one rotor. As a letter passes through each rotor +twice, the argument \identifier{direction} determines which way the +substitution is applied. +\stopparagraph +--ichd]]-- + local do_do_encode_char = function (char, rotor, direction) + local rw = rotor.wiring + local rs = rotor.state + local result = char + if direction then -- from + result = (result + rs - 1) % 26 + 1 + result = rw.from[result] + result = (result - rs - 1) % 26 + 1 + else -- to + result = (result + rs - 1) % 26 + 1 + result = rw.to[result] + result = (result - rs - 1) % 26 + 1 + end + return result + end + +--[[ichd-- +\startparagraph +Behind the plugboard, every character undergoes seven substitutions: +two for each rotor plus the central one through the reflector. The +function \luafunction{do_encode_char}, although it returns the final +result only, keeps every intermediary step inside a table for debugging +purposes. This may look inefficient but is actually a great advantage +whenever something goes wrong. +\stopparagraph +--ichd]]-- + --- ra -> rb -> rc -> ukw -> rc -> rb -> ra + local do_encode_char = function (rotors, reflector, char) + local rc, rb, ra = rotors[1], rotors[2], rotors[3] + local steps = { [0] = char } + -- + steps[1] = do_do_encode_char(steps[0], ra, true) + steps[2] = do_do_encode_char(steps[1], rb, true) + steps[3] = do_do_encode_char(steps[2], rc, true) + steps[4] = reflector_wiring[reflector][steps[3]] + steps[5] = do_do_encode_char(steps[4], rc, false) + steps[6] = do_do_encode_char(steps[5], rb, false) + steps[7] = do_do_encode_char(steps[6], ra, false) + emit(2, pprint_encoding_scheme) + emit(2, pprint_encoding, steps) + return steps[7] + end + +--[[ichd-- +\startparagraph +Before an input character is passed on to the actual encoding routing, +the function \luafunction{encode_char} matches it agains the latin +alphabet. +Characters failing this test are either passed through or ignored, +depending on the machine option \identifier{other_chars}. +Also, the counter of encoded characters is incremented at this stage +and some pretty printer hooks reside here. +\stopparagraph + +\startparagraph +\luafunction{encode_char} contributes only one element of the encoding +procedure: the plugboard (\emph{Steckerbrett}). +Like the rotors described above, a character passed through this +device twice; the plugboard marks the beginning and end of every step. +For debugging purposes, the first substitution is stored in a separate +local variable, \identifier{pb_char}. +\stopparagraph +--ichd]]-- + + local encode_char = function (machine, char) + machine.step = machine.step + 1 + machine:rotate() + local pb = machine.plugboard + char = letter_to_value[char] + local pb_char = pb[char] -- first plugboard substitution + emit(2, pprint_step, machine.step, char, pb_char) + emit(3, pprint_rotor_scheme) + emit(3, pprint_rotor, machine.rotors[1]) + emit(3, pprint_rotor, machine.rotors[2]) + emit(3, pprint_rotor, machine.rotors[3]) + char = do_encode_char(machine.rotors, + machine.reflector, + pb_char) + return value_to_letter[pb[char]] -- second plugboard substitution + end + + local get_random_pattern = function () + local a, b, c + = mathrandom(1,26), mathrandom(1,26), mathrandom(1,26) + return value_to_letter[a] + .. value_to_letter[b] + .. value_to_letter[c] + end + + local pattern_to_state = function (pat) + return { + letter_to_value[stringsub(pat, 1, 1)], + letter_to_value[stringsub(pat, 2, 2)], + letter_to_value[stringsub(pat, 3, 3)], + } + end + + local set_state = function (machine, state) + local rotors = machine.rotors + for i=1, 3 do + rotors[i].state = state[i] - 1 + end + end + +--[[ichd-- +\startparagraph +When \modulename{Enigma} is called from \TEX, the encoding +proceeds character by character as we iterate one node at a time. +\luafunction{encode_string} is a wrapper for use with strings, e.~g. in +the mtx-script (\at{page}[sec:fun]). +It handles iteration and extraction of successive characters from the +sequence. +\stopparagraph +--ichd]]-- + local encode_string = function (machine, str) --, pattern) + local result = { } + for char in utfcharacters(str) do + local tmp = machine:encode(char) + if tmp ~= false then + if type(tmp) == "table" then + for i=1, #tmp do + result[#result+1] = tmp[i] + end + else + result[#result+1] = tmp + end + end + end + machine:processed_chars() + return tableconcat(result) + end +--[[ichd-- +\stopdocsection +--ichd]]-- + +--[[ichd-- +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\startdocsection[title=Initialization string parser] +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\placetable[here][]{Initialization strings}{% + \bTABLE + \bTR + \bTD Reflector \eTD + \bTD[nc=3] Rotor \eTD + \bTD[nc=3] Initial \eTD + \bTD[nc=10] Plugboard wiring \eTD + \eTR + \eTR + \bTR + \bTD in slot \eTD + \bTD[nc=3] setting \eTD + \bTD[nc=3] rotor \eTD + \eTR + \bTR + \bTD \eTD + \bTD 1 \eTD\bTD 2 \eTD\bTD 3 \eTD + \bTD 1 \eTD\bTD 2 \eTD\bTD 3 \eTD + \bTD 1 \eTD\bTD 2 \eTD\bTD 3 \eTD\bTD 4 \eTD\bTD 5 \eTD + \bTD 6 \eTD\bTD 7 \eTD\bTD 8 \eTD\bTD 9 \eTD\bTD 10 \eTD + \eTR + \bTR + \bTD B \eTD + \bTD I \eTD\bTD IV \eTD\bTD III \eTD + \bTD 16 \eTD\bTD 26 \eTD\bTD 08 \eTD + \bTD AD \eTD\bTD CN \eTD\bTD ET \eTD + \bTD FL \eTD\bTD GI \eTD\bTD JV \eTD + \bTD KZ \eTD\bTD PU \eTD\bTD QY \eTD + \bTD WX \eTD + \eTR + \eTABLE +} +--ichd]]-- + local roman_digits = { + i = 1, I = 1, + ii = 2, II = 2, + iii = 3, III = 3, + iv = 4, IV = 4, + v = 5, V = 5, + } + + local p_init = P{ + "init", + init = V"whitespace"^-1 * Ct(V"do_init"), + do_init = (V"reflector" * V"whitespace")^-1 + * V"rotors" * V"whitespace" + * V"ring" + * (V"whitespace" * V"plugboard")^-1 + , + reflector = Cg(C(R("ac","AC")) / stringlower, "reflector") + , + + rotors = Cg(Ct(V"rotor" * V"whitespace" + * V"rotor" * V"whitespace" + * V"rotor"), + "rotors") + , + rotor = Cs(V"roman_five" / roman_digits + + V"roman_four" / roman_digits + + V"roman_three" / roman_digits + + V"roman_two" / roman_digits + + V"roman_one" / roman_digits) + , + roman_one = P"I" + P"i", + roman_two = P"II" + P"ii", + roman_three = P"III" + P"iii", + roman_four = P"IV" + P"iv", + roman_five = P"V" + P"v", + + ring = Cg(Ct(V"double_digit" * V"whitespace" + * V"double_digit" * V"whitespace" + * V"double_digit"), + "ring") + , + double_digit = C(R"02" * R"09"), + + plugboard = Cg(V"do_plugboard", "plugboard"), + --- no need to enforce exactly ten substitutions + --do_plugboard = Ct(V"letter_combination" * V"whitespace" + -- * V"letter_combination" * V"whitespace" + -- * V"letter_combination" * V"whitespace" + -- * V"letter_combination" * V"whitespace" + -- * V"letter_combination" * V"whitespace" + -- * V"letter_combination" * V"whitespace" + -- * V"letter_combination" * V"whitespace" + -- * V"letter_combination" * V"whitespace" + -- * V"letter_combination" * V"whitespace" + -- * V"letter_combination") + do_plugboard = Ct(V"letter_combination" + * (V"whitespace" * V"letter_combination")^0) + , + letter_combination = C(R("az", "AZ") * R("az", "AZ")), + + whitespace = S" \n\t\v"^1, + } + + +--[[ichd-- +\stopdocsection +--ichd]]-- + +--[[ichd-- +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\startdocsection[title=Initialization routines] +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\startparagraph +The plugboard is implemented as a pair of hash tables. +\stopparagraph +--ichd]]-- + local get_plugboard_substitution = function (p) + --- Plugboard wirings are symmetrical, thus we have one table for + --- each direction. + local tmp, result = { }, { } + for _, str in next, p do + local one, two = stringlower(stringsub(str, 1, 1)), + stringlower(stringsub(str, 2)) + tmp[one] = two + tmp[two] = one + end + local n_letters = 26 + + local lv = letter_to_value + for n=1, n_letters do + local letter = value_to_letter[n] + local sub = tmp[letter] or letter + -- Map each char either to the plugboard substitution or itself. + result[lv[letter]] = lv[sub or letter] + end + return result + end + +--[[ichd-- +\startparagraph +Initialization of the rotors requires some precautions to be taken. +The most obvious of which is adjusting the displacement of its wiring +by the ring setting. +\stopparagraph +\startparagraph +Another important task is to store the notch position in order for it +to be retrievable by the rotation subroutine at a later point. +\stopparagraph +\startparagraph +The actual bidirectional mapping is implemented as a pair of tables. +The initial order of letters, before the ring shift is applied, is +alphabetical on the input (right, “from”) side and, on the output +(left, “to”) side taken by the hard wired correspondence as specified +in the rotor wirings above. +NB the descriptions in terms of “output” and “input” directions is +misleading in so far as during any encoding step the electricity will +pass through every rotor in both ways. +Hence, the “input” (right, from) direction literally applies only to +the first half of the encoding process between plugboard and reflector. +\stopparagraph +\startparagraph +The function \luafunction{do_get_rotor} creates a single rotor instance +and populates it with character mappings. The \identifier{from} and +\identifier{to} subfields of its \identifier{wiring} field represent +the wiring in the respective directions. +This initital wiring was specified in the corresponding +\identifier{raw_rotor_wiring} table; the ringshift is added modulo the +alphabet size in order to get the correctly initialized rotor. +\stopparagraph +--ichd]]-- + local do_get_rotor = function (raw, notch, ringshift) + local rotor = { + wiring = { + from = { }, + to = { }, + }, + state = 0, + notch = notch, + } + local w = rotor.wiring + for from=1, 26 do + local to = letter_to_value[stringsub(raw, from, from)] + --- The shift needs to be added in both directions. + to = (to + ringshift - 1) % 26 + 1 + from = (from + ringshift - 1) % 26 + 1 + rotor.wiring.from[from] = to + rotor.wiring.to [to ] = from + end + --table.print(rotor, "rotor") + return rotor + end + +--[[ichd-- +\startparagraph +Rotors are initialized sequentially accordings to the initialization +request. +The function \luafunction{get_rotors} walks over the list of +initialization instructions and calls \luafunction{do_get_rotor} for +the actual generation of the rotor table. Each rotor generation request +consists of three elements: +\stopparagraph +\startitemize[n] + \item the choice of rotor (one of five), + \item the notch position of said rotor, and + \item the ring shift. +\stopitemize +--ichd]]-- + local get_rotors = function (rotors, ring) + local s, r = { }, { } + for n=1, 3 do + local nr = tonumber(rotors[n]) + local ni = tonumber(ring[n]) - 1 -- “1” means shift of zero + r[n] = do_get_rotor(raw_rotor_wiring[nr], notches[nr], ni) + s[n] = ni + end + return r, s + end + + local decode_char = encode_char -- hooray for involutory ciphers + +--[[ichd-- +\startparagraph +The function \luafunction{encode_general} is an intermediate step for +the actual single-character encoding / decoding routine +\luafunction{enchode_char}. +Its purpose is to ensure encodability of a given character before +passing it along. +Characters are first checked against the replacement table +\identifier{pp_substitutions} (see \at{page}[listing:preproc]). +For single-character replacements the function returns the encoded +character (string). +However, should the replacement turn out to consist of more than one +character, each one will be encoded successively, yielding a list. +\stopparagraph +--ichd]]-- + local encode_general = function (machine, chr) + local chr = utf8lower(chr) + local replacement + = pp_substitutions[chr] or letter_to_value[chr] and chr + if not replacement then + if machine.other_chars then + return chr + else + return false + end + end + + if utf8len(replacement) == 1 then + return encode_char(machine, replacement) + end + local result = { } + for new_chr in utfcharacters(replacement) do + result[#result+1] = encode_char(machine, new_chr) + end + return result + end + + local process_message_key + local alpha = R"az" + local alpha_dec = alpha / letter_to_value + local whitespace = S" \n\t\v" + local mkeypattern = Ct(alpha_dec * alpha_dec * alpha_dec) + * whitespace^0 + * C(alpha * alpha *alpha) + process_message_key = function (machine, message_key) + message_key = stringlower(message_key) + local init, three = lpegmatch(mkeypattern, message_key) + -- to be implemented + end + + local decode_string = function (machine, str, message_key) + machine.kenngruppe, str = stringsub(str, 3, 5), stringsub(str, 6) + machine:process_message_key(message_key) + local decoded = encode_string(machine, str) + return decoded + end + + local testoptions = { + size = 42, + + } + local generate_header = function (options) + end + + local processed_chars = function (machine) + emit(1, pprint_machine_step, machine.step, machine.name) + end + +--[[ichd-- +\startparagraph +The day key is entrusted to the function \luafunction{handle_day_key}. +If the day key is the empty string or \type{nil}, it will ask for a key +on the terminal. (Cf. below, \at{page}[listing:ask_for_day_key].) +Lesson: don’t forget providing day keys in your setups when running in +batch mode. +\stopparagraph +--ichd]]-- + local handle_day_key handle_day_key = function (dk, name, old) + local result + if not dk or dk == "" then + dk = ask_for_day_key(name, old) + end + result = lpegmatch(p_init, dk) + result.reflector = result.reflector or "b" + -- If we don’t like the key we’re going to ask again. And again.... + return result or handle_day_key(nil, name, dk) + end + +--[[ichd-- +\startparagraph +The enigma encoding is restricted to an input -- and, naturally, output +-- alphabet of exactly twenty-seven characters. Obviously, this would +severely limit the set of encryptable documents. For this reason the +plain text would be \emph{preprocessed} prior to encoding, removing +spaces and substituting a range of characters, e.\,g. punctuation, with +placeholders (“X”) from the encodable spectrum. See above +\at{page}[listing:preproc] for a comprehensive list of substitutions. +\stopparagraph + +\startparagraph +The above mentioned preprocessing, however, does not even nearly extend +to the whole unicode range that modern day typesetting is expected to +handle. Thus, sooner or later an Enigma machine will encounter +non-preprocessable characters and it will have to decide what to do +with them. The Enigma module offers two ways to handle this kind of +situation: \emph{drop} those characters, possibly distorting the +deciphered plain text, or to leave them in, leaving hints behind as to +the structure of the encrypted text. None of these is optional, so it +is nevertheless advisable to not include non-latin characters in the +plain text in the first place. The settings key +\identifier{other_chars} (type boolean) determines whether we will keep +or drop offending characters. +\stopparagraph +--ichd]]-- + + new = function (name, args) + local setup_string, pattern = args.day_key, args.rotor_setting + local raw_settings = handle_day_key(setup_string, name) + local rotors, ring = + get_rotors(raw_settings.rotors, raw_settings.ring) + local plugboard + = raw_settings.plugboard + and get_plugboard_substitution(raw_settings.plugboard) + or get_plugboard_substitution{ } + local machine = { + name = name, + step = 0, -- n characters encoded + init = { + rotors = raw_settings.rotors, + ring = raw_settings.ring + }, + rotors = rotors, + ring = ring, + state = init_state, + other_chars = args.other_chars, + spacing = args.spacing, + ---> a>1, b>2, c>3 + reflector = letter_to_value[raw_settings.reflector], + plugboard = plugboard, + --- functionality + rotate = rotate, + --process_message_key = process_message_key, + encode_string = encode_string, + encode_char = encode_char, + encode = encode_general, + decode_string = decode_string, + decode_char = decode_char, + set_state = set_state, + processed_chars = processed_chars, + --- -- hackish but occasionally useful + __raw = raw_settings + --- + } --- machine + local init_state + = pattern_to_state(pattern or get_random_pattern()) + emit(1, pprint_init, init_state) + machine:set_state(init_state) + + --table.print(machine.rotors) + emit(1, pprint_new_machine, machine) + return machine + end + +end +--[[ichd-- +\stopdocsection +--ichd]]-- + + +--[[ichd-- +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\startdocsection[title=Setup Argument Handling] +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +--ichd]]-- +do +--[[ichd-- +\startparagraph +As the module is intended to work both with the Plain and \LATEX\ +formats as well as \CONTEXT, we can’t rely on format dependent setups. +Hence the need for an argument parser. Should be more efficient anyways +as all the functionality resides in Lua. +\stopparagraph +--ichd]]-- + + local p_args = P{ + "args", + args = Cf(Ct"" * (V"kv_pair" + V"emptyline")^0, rawset), + kv_pair = Cg(V"key" + * V"separator" + * (V"value" * V"final" + + V"empty")) + * V"rest_of_line"^-1 + , + key = V"whitespace"^0 * C(V"key_char"^1), + key_char = (1 - V"whitespace" - V"eol" - V"equals")^1, + separator = V"whitespace"^0 * V"equals" * V"whitespace"^0, + empty = V"whitespace"^0 * V"comma" * V"rest_of_line"^-1 + * Cc(false) + , + value = C((V"balanced" + (1 - V"final"))^1), + final = V"whitespace"^0 * V"comma" + V"rest_of_string", + rest_of_string = V"whitespace"^0 + * V"eol_comment"^-1 + * V"eol"^0 + * V"eof" + , + rest_of_line = V"whitespace"^0 * V"eol_comment"^-1 * V"eol", + eol_comment = V"comment_string" * (1 - (V"eol" + V"eof"))^0, + comment_string = V"lua_comment" + V"TeX_comment", + TeX_comment = V"percent", + lua_comment = V"double_dash", + emptyline = V"rest_of_line", + + balanced = V"balanced_brk" + V"balanced_brc", + balanced_brk = V"lbrk" + * (V"balanced" + (1 - V"rbrk"))^0 + * V"rbrk" + , + balanced_brc = V"lbrc" + * (V"balanced" + (1 - V"rbrc"))^0 + * V"rbrc" + , + -- Terminals + eol = P"\n\r" + P"\r\n" + P"\n" + P"\r", + eof = -P(1), + whitespace = S" \t\v", + equals = P"=", + dot = P".", + comma = P",", + dash = P"-", double_dash = V"dash" * V"dash", + percent = P"%", + lbrk = P"[", rbrk = P"]", + lbrc = P"{", rbrc = P"}", + } + + +--[[ichd-- +\startparagraph +In the next step we process the arguments, check the input for sanity +etc. The function \luafunction{parse_args} will test whether a value +has a sanitizer routine and, if so, apply it to its value. +\stopparagraph +--ichd]]-- + + local boolean_synonyms = { + ["1"] = true, + doit = true, + indeed = true, + ok = true, + ["⊤"] = true, + ["true"] = true, + yes = true, + } + local toboolean + = function (value) return boolean_synonyms[value] or false end + local alpha = R("az", "AZ") + local digit = R"09" + local space = S" \t\v" + local ans = alpha + digit + space + local p_ans = Cs((ans + (1 - ans / ""))^1) + local alphanum_or_space = function (str) + if type(str) ~= "string" then return nil end + return lpegmatch(p_ans, str) + end + local ensure_int = function (n) + n = tonumber(n) + if not n then return 0 end + return mathfloor(n + 0.5) + end + p_alpha = Cs((alpha + (1 - alpha / ""))^1) + local ensure_alpha = function (s) + s = tostring(s) + return lpegmatch(p_alpha, s) + end + + local sanitizers = { + other_chars = toboolean, -- true = keep, false = drop + spacing = toboolean, + day_key = alphanum_or_space, + rotor_setting = ensure_alpha, + verbose = ensure_int, + } + enigma.parse_args = function (raw) + local args = lpegmatch(p_args, raw) + for k, v in next, args do + local f = sanitizers[k] + if f then + args[k] = f(v) + else + -- OPTIONAL be fascist and permit only predefined args + args[k] = v + end + end + return args + end +--[[ichd-- +\startparagraph +If the machine setting lacks key settings then we’ll go ahead and ask +\reference[listing:ask_for_day_key]{}% +the user directly, hence the function \luafunction{ask_for_day_key}. +We abort after three misses lest we annoy the user \dots +\stopparagraph +--ichd]]-- + local max_tries = 3 + ask_for_day_key = function (name, old, try) + if try == max_tries then + iowrite[[ +Aborting. Entered invalid key three times. +]] + os.exit() + end + if old then + emit(0, key_invalid) + end + emit(0, polite_key_request, name) + local result = ioread() + iowrite("\n") + return alphanum_or_space(result) or + ask_for_day_key(name, (try and try + 1 or 1)) + end +end + +--[[ichd-- +\stopdocsection +--ichd]]-- + +--[[ichd-- +\startdocsection[title=Callback] +\startparagraph +This is the interface to \TEX. We generate a new callback handler for +each defined Enigma machine. \CONTEXT\ delivers the head as third +argument of a callback only (...‽), so we’ll have to do some variable +shuffling on the function side. +\stopparagraph + +\startparagraph +When grouping output into the traditional blocks of five letters we +insert space nodes. As their properties depend on the font we need to +recreate the space item for every paragraph. Also, as \CONTEXT\ does +not preload a font we lack access to font metrics before +\type{\starttext}. Thus creating the space earlier will result in an +error. +The function \luafunction{generate_space} will be called inside the +callback in order to get an appropriate space glue. +\stopparagraph +--ichd]]-- + +local generate_space = function ( ) + local current_fontparms = font.getfont(font.current()).parameters + local space_node = nodenew(GLUE_NODE) + space_node.spec = nodenew(GLUE_SPEC_NODE) + space_node.spec.width = current_fontparms.space + space_node.spec.shrink = current_fontparms.space_shrink + space_node.spec.stretch = current_fontparms.space_stretch + return space_node +end + +--[[ichd-- +\startparagraph +\useURL[khaled_hosny_texsx] [http://tex.stackexchange.com/a/11970] + [] [tex.sx] +Registering a callback (“node attribute”?, “node task”?, “task +action”?) in \CONTEXT\ is not straightforward, let alone documented. +The trick is to create, install and register a handler first in order +to use it later on \dots\ many thanks to Khaled Hosny, who posted an +answer to \from[khaled_hosny_texsx]. +\stopparagraph +--ichd]]-- + +local new_callback = function (machine, name) + enigma.machines [name] = machine + local format_is_context = format_is_context + local current_space_node + local mod_5 = 0 + + --- First we need to choose an insertion method. If autospacing is + --- requested, a space will have to be inserted every five + --- characters. The rationale behind using differend functions to + --- implement each method is that it should be faster than branching + --- for each character. + local insert_encoded + + if machine.spacing then -- auto-group output + insert_encoded = function (head, n, replacement) + local insert_glyph = nodecopy(n) + if replacement then -- inefficient but bulletproof + insert_glyph.char = utf8byte(replacement) + --print(utf8char(n.char), "=>", utf8char(insertion.char)) + end + --- if we insert a space we need to return the + --- glyph node in order to track positions when + --- replacing multiple nodes at once (e.g. ligatures) + local insertion = insert_glyph + mod_5 = mod_5 + 1 + if mod_5 > 5 then + mod_5 = 1 + insertion = nodecopy(current_space_node) + insertion.next, insert_glyph.prev = insert_glyph, insertion + end + if head == n then --> replace head + local succ = head.next + if succ then + insert_glyph.next, succ.prev = succ, insert_glyph + end + head = insertion + else --> replace n + local pred, succ = n.prev, n.next + pred.next, insertion.prev = insertion, pred + if succ then + insert_glyph.next, succ.prev = succ, insert_glyph + end + end + + --- insertion becomes the new head + return head, insert_glyph -- so we know where to insert + end + else + + insert_encoded = function (head, n, replacement) + local insertion = nodecopy(n) + if replacement then + insertion.char = utf8byte(replacement) + end + if head == n then + local succ = head.next + if succ then + insertion.next, succ.prev = succ, insertion + end + head = insertion + else + nodeinsert_before(head, n, insertion) + noderemove(head, n) + end + return head, insertion + end + end + + --- The callback proper starts here. + local aux aux = function (head, recurse) + if recurse == nil then recurse = 0 end + for n in nodetraverse(head) do + local nid = n.id + --print(utf8char(n.char), n) + if nid == GLYPH_NODE then + local chr = utf8char(n.char) + --print(chr, n) + local replacement = machine:encode(chr) + --print(chr, replacement, n) + local treplacement = replacement and type(replacement) + --if replacement == false then + if not replacement then + noderemove(head, n) + elseif treplacement == "string" then + --print(head, n, replacement) + head, _ = insert_encoded(head, n, replacement) + elseif treplacement == "table" then + local current = n + for i=1, #replacement do + head, current = insert_encoded(head, current, replacement[i]) + end + end + elseif nid == GLUE_NODE then + if n.subtype ~= 15 then -- keeping the parfillskip + noderemove(head, n) + end + elseif IGNORE_NODES[nid] then + -- drop spaces and kerns + noderemove(head, n) + elseif nid == DISC_NODE then + --- ligatures need to be resolved if they are characters + local npre, npost = n.pre, n.post + if nodeid(npre) == GLYPH_NODE and + nodeid(npost) == GLYPH_NODE then + if npre.char and npost.char then -- ligature between glyphs + local replacement_pre = machine:encode(utf8char(npre.char)) + local replacement_post = machine:encode(utf8char(npost.char)) + insert_encoded(head, npre, replacement_pre) + insert_encoded(head, npost, replacement_post) + else -- hlists or whatever + -- pass + --noderemove(head, npre) + --noderemove(head, npost) + end + end + noderemove(head, n) + elseif nid == HLIST_NODE or nid == VLIST_NODE then + if nodelength(n.list) > 0 then + n.list = aux(n.list, recurse + 1) + end +-- else +-- -- TODO other node types +-- print(n) + end + end + nodeslide(head) + return head + end -- callback auxiliary + + --- Context requires + --- × argument shuffling; a properly registered “action” gets the + --- head passed as its third argument + --- × hacking our way around the coupling of pre_linebreak_filter + --- and hpack_filter; background: + --- http://www.ntg.nl/pipermail/ntg-context/2012/067779.html + local cbk = function (a, _, c) + local head + current_space_node = generate_space () + mod_5 = 0 + if format_is_context == true then + head = c + local cbk_env = get_debug_info(4) -- no getenv in lua 5.2 + --inspect(cbk_env) + if cbk_env.func == nodes.processors.pre_linebreak_filter then + -- how weird is that? + return aux(head) + end + return head + end + head = a + return aux(head) + end + + if format_is_context then + local cbk_id = "enigma_" .. name + enigma.callbacks[name] = nodesinstallattributehandler{ + name = cbk_id, + namespace = thirddata.enigma, + processor = cbk, + } + local cbk_location = "thirddata.enigma.callbacks." .. name + nodestasksappendaction("processors", + --"characters", + --"finalizers", + --- this one is tagged “for users” + --- (cf. node-tsk.lua) + "before", + cbk_location) + nodestasksdisableaction("processors", cbk_location) + else + enigma.callbacks[name] = cbk + end +end + +--[[ichd-- +\startparagraph +Enigma\reference[listing:retrieve]{} machines can be copied and derived +from one another at will, cf. the \texmacro{defineenigma} on +\at{page}[listing:define]. Two helper functions residing inside the +\identifier{thirddata.enigma} namespace take care of these actions: +\luafunction{save_raw_args} and \luafunction{retrieve_raw_args}. As +soon as a machine is defined, we store its parsed options inside the +table \identifier{configurations} for later reference. For further +details on the machine derivation mechanism see +\at{page}[listing:inherit]. +\stopparagraph +--ichd]]-- +local configurations = { } + +local save_raw_args = function (conf, name) + local current = configurations[name] or { } + for k, v in next, conf do + current[k] = v + end + configurations[name] = current +end + +local retrieve_raw_args = function (name) + local cn = configurations[name] + return cn and tablecopy(cn) or { } +end + +enigma.save_raw_args = save_raw_args +enigma.retrieve_raw_args = retrieve_raw_args + + +--[[ichd-- +\startparagraph +The function \luafunction{new_machine} instantiates a table containing +the complete specification of a workable \emph{Enigma} machine and +other metadata. The result is intended to be handed over to the +callback creation mechanism (\luafunction{new_callback}). However, the +arguments table is usally stored away in the +\identifier{thirddata.enigma} namespace anyway +(\luafunction{save_raw_args}), so that the specification of any machine +can be inherited by some new setup later on. +\stopparagraph +--ichd]]-- +local new_machine = function (name) + local args = configurations[name] + --table.print(configurations) + verbose_level = args and args.verbose or verbose_level + local machine = new(name, args) + return machine +end + +enigma.new_machine = new_machine +enigma.new_callback = new_callback + +--[[ichd-- +\stopdocsection +--ichd]]-- + +-- vim:ft=lua:sw=2:ts=2:tw=71:expandtab diff --git a/Master/texmf-dist/tex/latex/enigma/enigma.sty b/Master/texmf-dist/tex/latex/enigma/enigma.sty new file mode 100644 index 00000000000..07bb8e78c93 --- /dev/null +++ b/Master/texmf-dist/tex/latex/enigma/enigma.sty @@ -0,0 +1,7 @@ +\ProvidesPackage + {enigma} + [2013/03/28 Enigma Document Encryption] +\RequirePackage{luatexbase} +\input{enigma} +\endinput +% vim:ft=tex:sw=2:ts=2:expandtab:tw=71 diff --git a/Master/texmf-dist/tex/plain/enigma/enigma.tex b/Master/texmf-dist/tex/plain/enigma/enigma.tex new file mode 100644 index 00000000000..d18b72ffa18 --- /dev/null +++ b/Master/texmf-dist/tex/plain/enigma/enigma.tex @@ -0,0 +1,130 @@ +\newif\ifenigmaisrunningplain +\ifcsname ver@enigma.sty\endcsname\else + \enigmaisrunningplaintrue + \input{luatexbase.sty} + \catcode`\@=11 +% \else latex +\fi +\catcode`\_=11 % There’s no reason why this shouldn’t be the case. +\catcode`\!=11 +%D Nice tool from luat-ini.mkiv. This really helps with those annoying +%D string separators of Lua’s that clutter the source. +% this permits \typefile{self} otherwise nested b/e sep problems +\def\luastringsep{===} +\edef\!!bs{[\luastringsep[} +\edef\!!es{]\luastringsep]} +%D \startdocsection[title=Prerequisites] +%D \startparagraph +%D Package loading and the namespacing issue are commented on in +%D \identifier{enigma.lua}. +%D \stopparagraph +\directlua{ + packagedata = packagedata or { } + dofile(kpse.find_file\!!bs enigma.lua\!!es) +} + +%D \startparagraph +%D First, create somthing like \CONTEXT’s asciimode. We found +%D \texmacro{newluatexcatcodetable} in \identifier{luacode.sty} and it +%D seems to get the job done. +%D \stopparagraph +\newluatexcatcodetable \enigmasetupcatcodes +\bgroup + \def\escapecatcode {0} + \def\begingroupcatcode {1} + \def\endgroupcatcode {2} + \def\spacecatcode {10} + \def\lettercatcode {11} + \setluatexcatcodetable\enigmasetupcatcodes { + \catcode`\^^I = \spacecatcode % tab + \catcode`\ = \spacecatcode + \catcode`\{ = \begingroupcatcode + \catcode`\} = \endgroupcatcode + \catcode`\^^L = \lettercatcode % form feed + \catcode`\^^M = \lettercatcode % eol + } +\egroup +%D \stopdocsection + +%D \startdocsection[title=Setups] +%D \startparagraph +%D Once the proper catcodes are in place, the setup macro +%D \texmacro{do_setup_enigma} doesn’t to anything besides passing stuff +%D through to Lua. +%D \stopparagraph +\def\do_setup_enigma#1{% + \directlua{ + local enigma = packagedata.enigma + local current_args = enigma.parse_args(\!!bs\detokenize{#1}\!!es) + enigma.save_raw_args(current_args, \!!bs\current_enigma_id\!!es) + enigma.new_callback( + enigma.new_machine(\!!bs\current_enigma_id\!!es), + \!!bs\current_enigma_id\!!es) + }% + \egroup% +} + +%D The module setup \texmacro{setupenigma} expects key=value, notation. +%D All the logic is at the Lua end, not much to see here … +\def\setupenigma#1{% + \bgroup + \edef\current_enigma_id{#1} + \luatexcatcodetable \enigmasetupcatcodes + \do_setup_enigma% +} +%D \stopdocsection + +%D \startdocsection[title=Encoding Macros] +%D \startparagraph +%D The environment of \texmacro{begin} and +%D \texmacro{end} toggles Enigma encoding. +%D (Regarding environment delimiters we adhere to Knuth’s +%D practice of prefixing with \type{begin}/\type{end}.) +%D \stopparagraph + +\def\e!start{begin} %{start} +\def \e!stop{end} %{stop} +\edef\c!pre_linebreak_filter{pre_linebreak_filter} +\def\do_define_enigma#1{% + \@EA\gdef\csname \e!start\current_enigma_id\endcsname{% + \endgraf + \bgroup% + \directlua{% + if packagedata.enigma and + packagedata.enigma.machines[ \!!bs#1\!!es] then + luatexbase.add_to_callback( + \!!bs\c!pre_linebreak_filter\!!es, + packagedata.enigma.callbacks[ \!!bs#1\!!es], + \!!bs#1\!!es) + else + print\!!bs ENIGMA: No machine of that name: #1!\!!es + os.exit() + end + }% + }% + \@EA\gdef\csname \e!stop\current_enigma_id\endcsname{% + \endgraf + \directlua{ + luatexbase.remove_from_callback( + \!!bs\c!pre_linebreak_filter\!!es, + \!!bs#1\!!es) + packagedata.enigma.machines[ \!!bs#1\!!es]:processed_chars() + }% + \egroup% + }% +} + +\def\defineenigma#1{% + \begingroup + \let\@EA\expandafter + \edef\current_enigma_id{#1}% + \@EA\do_define_enigma\@EA{\current_enigma_id}% + \endgroup% +} + +%D \stopdocsection + +\catcode`\_=8 % \popcatcodes +\catcode`\!=12 % reserved according to source2e +\ifenigmaisrunningplain\catcode`\@=12\fi +% vim:ft=plaintex:sw=2:ts=2:expandtab:tw=71 -- cgit v1.2.3