[Tex/LaTex] Encrypt and decrypt parts of text in a LaTeX source file

knitrpackage-writingr

Assume that you have some text in parts of your LaTeX document that you only want people with a password to see. A reason could e.g. be a case with an guiding answer. You want to make the case public but the answer only to teachers.

Anyone adware of a package that could do something like:

\usepackage[password=<my password>]{decrypt}
\begin{document} 
<Public text>
\begin{private}
<Private encrypted text>
\end{private}
\end{document} 

When running e.g. pdfLaTeX the encrypted text will be decrypted and show in the resulting pdf. I guess that some kind of preprocessing is needed to replace the private text with encrypted text. An idea could be to use R and knitr for this (an example online is http://textmechanic.com/Encryption-Generator.html)

Best Answer

Updated answer

This is Enigma.

Notice that Enigma is symmetric. Thus you can encode and decode at the same place. In the following example, the first input AN ENIGMA MACHINE... comes from the Wikipedia article. And then I copied and pasted the output ISWXACOIOIGKAXHF...to be the second input. Thus we got ANENIGMAMACHINE as the second output, which is what we inputed at the beginning.

\documentclass{article}
\usepackage{pgfmath}

% These counters are registers. One needs to assign the initial values of \rotorphasecounts, which function as the password.

\newcount\crycount
\newcount\plugboardcount
\newcount\rotorcounti
\newcount\rotorcountii
\newcount\rotorcountiii
\newcount\rotorphasecounti
\newcount\rotorphasecountii
\newcount\rotorphasecountiii
\newcount\reflectorcount

% This is the main function. It reads characters until it reaches \end.

\def\private#1{%
    \ifx#1\end
        \expandafter\end
    \else

% First of all, convert capital letters to numbers. We want A becomes 0 and Z becomes 25.

        \crycount`#1
        \pgfmathsetmacro\crystringi{int(Mod(\crycount-65,26))}

% Now the number passes through the Plugboard, which is a part of the Enigma.

        \plugboard\crystringii\crystringi

% Now the number passes through the three Rotors. It could be four if you want to.

        \rotori\crystringiii\crystringii
        \rotorii\crystringiv\crystringiii
        \rotoriii\crystringv\crystringiv

% Now the number reaches the Reflector.

        \reflector\crystringvi\crystringv

% So then the number goes backwards, passing through the Rotors and the Plugboard.

        \inverserotoriii\crystringvii\crystringvi
        \inverserotorii\crystringviii\crystringvii
        \inverserotori\crystringix\crystringviii
        \plugboard\crystringx\crystringix

% Convert the number to the hexadecimal code of the corresponding capital letters. For instance 0 means A, so it should be ^^65.

        \pgfmathsetmacro\crystringxi{hex(\crystringx+65)}
        \xdef\crystring{\crystring\string^\string^\crystringxi\string\allowbreak{}}
        \expandafter\private
    \fi
}

% A Plugboard exchanges pairs of letters. In this case A and B are switched. So are C&D and E&F.

\def\plugboard#1#2{
    \plugboardcount#2\xdef#1{\ifcase\plugboardcount1\or0\or3\or2\or5\or4\or6\or7\or8\or9\or10\or11\or12\or13\or14\or15\or16\or17\or18\or19\or20\or21\or22\or23\or24\or25\fi}
}

% A Rotor permutes 26 letters in a fixed way. The funny part is, the first rotor rotates by 2π/26 every time a number passes by. The second rotates every time the first finishes a cycle. And so forth.

\def\rotori#1#2{
    \advance\rotorphasecounti1
    \pgfmathsetcount\rotorcounti{int(Mod(#2+\rotorphasecounti,26))}
    \xdef\rotortexti{\ifcase\rotorcounti1\or2\or3\or4\or0\or6\or7\or8\or9\or10\or11\or12\or13\or14\or15\or16\or17\or18\or19\or20\or21\or22\or23\or24\or25\or5\fi}
    \pgfmathsetmacro#1{int(Mod(\rotortexti-\rotorphasecounti,26))}
}
\def\rotorii#1#2{
    \ifnum\rotorphasecounti=26\rotorphasecounti0\advance\rotorphasecountii1\fi
    \pgfmathsetcount\rotorcountii{int(Mod(#2+\rotorphasecountii,26))}
    \xdef\rotortextii{\ifcase\rotorcountii1\or2\or3\or4\or0\or5\or6\or10\or11\or12\or7\or8\or9\or13\or14\or15\or16\or17\or18\or19\or20\or21\or22\or23\or24\or25\fi}
    \pgfmathsetmacro#1{int(Mod(\rotortextii-\rotorphasecountii,26))}
}
\def\rotoriii#1#2{
    \ifnum\rotorphasecountii=26\rotorphasecountii0\advance\rotorphasecountiii1\fi
    \pgfmathsetcount\rotorcountiii{int(Mod(#2+\rotorphasecountiii,26))}
    \xdef\rotortextiii{\ifcase\rotorcountiii1\or2\or3\or4\or0\or5\or6\or7\or8\or9\or10\or11\or12\or13\or14\or15\or16\or20\or21\or22\or23\or24\or25\or17\or18\or19\fi}
    \pgfmathsetmacro#1{int(Mod(\rotortextiii-\rotorphasecountiii,26))}
}

% The Reflector reflects the number. In the history it always reflected a number to a different one, and that made Enigma self-reciprocal, but unsafe.

\def\reflector#1#2{
    \reflectorcount#2\xdef#1{\ifcase\reflectorcount3\or10\or18\or0\or23\or11\or22\or8\or7\or19\or1\or5\or25\or20\or16\or24\or14\or21\or2\or9\or13\or17\or6\or4\or15\or12\fi}
}

% Physically there are only three rotors. But we still need to implement the macro for the opposite direction.

\def\inverserotoriii#1#2{
    \pgfmathsetcount\rotorcountiii{int(Mod(#2+\rotorphasecountiii,26))}
    \xdef\rotortextiii{\ifcase\rotorcountiii4\or0\or1\or2\or3\or5\or6\or7\or8\or9\or10\or11\or12\or13\or14\or15\or16\or23\or24\or25\or17\or18\or19\or20\or21\or22\fi}
    \pgfmathsetmacro#1{int(Mod(\rotortextiii-\rotorphasecountiii,26))}
}
\def\inverserotorii#1#2{
    \pgfmathsetcount\rotorcountii{int(Mod(#2+\rotorphasecountii,26))}
    \xdef\rotortextii{\ifcase\rotorcountii4\or0\or1\or2\or3\or5\or6\or10\or11\or12\or7\or8\or9\or13\or14\or15\or16\or17\or18\or19\or20\or21\or22\or23\or24\or25\fi}
    \pgfmathsetmacro#1{int(Mod(\rotortextii-\rotorphasecountii,26))}
}
\def\inverserotori#1#2{
    \pgfmathsetcount\rotorcounti{int(Mod(#2+\rotorphasecounti,26))}
    \xdef\rotortexti{\ifcase\rotorcounti4\or0\or1\or2\or3\or25\or5\or6\or7\or8\or9\or10\or11\or12\or13\or14\or15\or16\or17\or18\or19\or20\or21\or22\or23\or24\fi}
    \pgfmathsetmacro#1{int(Mod(\rotortexti-\rotorphasecounti,26))}
}

% Now we finish the definition.

\begin{document}

\def\crystring{}
\rotorphasecounti5
\rotorphasecountii8
\rotorphasecountiii13
\begin{private}
    AN ENIGMA MACHINE WAS A SERIES OF ELECTRO-MECHANICAL ROTOR CIPHER MACHINES DEVELOPED AND USED IN THE EARLY TO MID TWENTIETH CENTURY FOR COMMERCIAL AND MILITARY USAGE. ENIGMA WAS INVENTED BY THE GERMAN ENGINEER ARTHUR SCHERBIUS AT THE END OF WORLD WAR I.[1] EARLY MODELS WERE USED COMMERCIALLY FROM THE EARLY 1920S, AND ADOPTED BY MILITARY AND GOVERNMENT SERVICES OF SEVERAL COUNTRIES, MOST NOTABLY NAZI GERMANY BEFORE AND DURING WORLD WAR II.[2] SEVERAL DIFFERENT ENIGMA MODELS WERE PRODUCED, BUT THE GERMAN MILITARY MODELS ARE THE MOST COMMONLY RECOGNISED.
\end{private}
\newwrite\cryfile
\immediate\openout\cryfile=\jobname.cry
\immediate\write\cryfile{\crystring}
\immediate\closeout\cryfile
\input{\jobname.cry}

\bigskip

\def\crystring{}
\rotorphasecounti5
\rotorphasecountii8
\rotorphasecountiii13
\begin{private}
    ISWXACOIOIGKAXHFCQCQREARUMCRVRFMERXORFOIBAAIVBM UMEFASKWEOIFKABRPWRLRVMSNWQBAOMRWVBUKRMLEYLPUSV WPDNMPVSPKFXMPTULCXUFXKNHUVVBYRQOUBYVJWALMQMBSK SQVBURPBBYLEVLJVZGIJKVBVAURLVLBFLVGPUPJODPMFOVPGYA PUJJOSVLOHCNLPOZNUPYJPSFVUKJNJGZVOKNGSPARPUFLYYUSFW UOJMCSLYQZPGOMJHBZJLUCOXZOZAEQPZGHRWJWNOMHOTZJAX UMURPTNUPMLWWPUADJBLVPOVFAROSUWBOCYAJNTANOEPHTR MCKBAPRFHEBSVABRTZYMAYGNRBAPZARAYYCRKHFBOXARPZYG SBWVHIBHYZWRAVCBJFMXABUGVCPXRCSGPQVNRBMEWZVNVYV QZELWJNSYEZBSVKSWJEQDJKWJXYLBRLMWXTESFK
\end{private}
\newwrite\cryfile
\immediate\openout\cryfile=\jobname.cry
\immediate\write\cryfile{\crystring}
\immediate\closeout\cryfile
\input{\jobname.cry}

\end{document}

Comments

  • One can extend the macros so that one can use lower letters or even symbols in their text. At that time one needs to take care of category codes.
  • So far the \rotors I implemented are stupid. Because otherwise I have to calculate the \inverserotors carefully.
  • Enigma is by no means efficient. This is just an illustration of how complex could it be.

Old answer

I would like to propose my approach:

  • your ciphertext should be some unicode characters. (So you can compile it by XeLaTeX without password.)
  • One guesses the password by setting, say, \password="2600.
  • For every unicode character, \password is subtracted form its code point. For example =U+2655 becomes 55
  • the remaining number is printed to a file with ^^. That is, ^^55 is printed to a file
  • After all unicode characters are processed, the file is \inputed. Therefor finally becomes U.

Some efforts are left to you:

  • Now the decipher function D(♕,2600)='U' is quiet simple. You should be able to come up with a better one.
  • I am too lazy to pack macros. Do it yourself.

\documentclass{article}
\usepackage{fontspec}

\newcount\password
\newcount\crycount
\newcount\crycounta
\newcount\crycountb

\def\private#1{%
    \ifx#1\end
        \expandafter\end
    \else
        \crycount`#1
        % ↓↓↓ some math involving \crycount and \password
        \advance\crycount-\password
        \crycounta\crycount\crycountb\crycount\divide\crycounta16\multiply\crycounta16\advance\crycountb-\crycounta\divide\crycounta16
    \xdef\crychara{\ifcase\crycounta0\or1\or2\or3\or4\or5\or6\or7\or8\or9\or a\or b\or c\or d\or e\else f\fi}
    \xdef\crycharb{\ifcase\crycountb0\or1\or2\or3\or4\or5\or6\or7\or8\or9\or a\or b\or c\or d\or e\else f\fi}
        % ↑↑↑ some math involving \crycount and \password
        \xdef\crystring{\crystring\string^\string^\crychara\crycharb}
        \expandafter\private
    \fi
}

\begin{document}

If key is set to be 2600, you can see the hint below:
\password"2600
\def\crystring{}
\begin{private}
    ♕♳♥☠♭♡♴♨♥♭♡♴♩♣♡♬☠♩♮♤♵♣♴♩♯♮☠♯♮☠☤♜♬♡♭♢♤♡☤☮
\end{private}

\newwrite\cryfile
\immediate\openout\cryfile=\jobname.cry
\immediate\write\cryfile{\crystring}
\immediate\closeout\cryfile
\input{\jobname.cry}

If key is not 2600, you see random characters:
\password"0
\def\crystring{}
\begin{private}
    ♕♳♥☠♭♡♴♨♥♭♡♴♩♣♡♬☠♩♮♤♵♣♴♩♯♮☠♯♮☠☤♜♬♡♭♢♤♡☤☮
\end{private}

\newwrite\cryfile
\immediate\openout\cryfile=\jobname.cry
\immediate\write\cryfile{\crystring}
\immediate\closeout\cryfile
\input{\jobname.cry}

\end{document}