A Drunken Family


Describing "booze.c", which can be used as PUKS (Proposed Universal Key Scheduler)

--- How it happened ---

        This essay will be centered about an ignorant layman's approach to the 
design of symmetric ciphers, and I apologize in advance for the terminology,
which may seem to some people simply strange, to others outright offensive.

        Some time ago I got hold of Bruce Schneier's book "Applied Cryptography",
and as it usually happens, along with the answers to some of my questions, I got
new questions, which are for the moment without answers.

        Main question - should the algorithm of the cipher be open for analysis 
or should it be unavailable to cryptanalyst? The consensus is that the algorithm
should be made public, and all the security must be based on the key. This seems 
reasonable, because such requirement closes the door for "snake-oil" promoters,
whose algorithms cannot be scrutinized.

        On the other hand, exact knowledge of the algorithm allows very finely 
tuned attacks, which exploit the minute details of the ciphering mechanism. 
Quoting Schneier: "...chosen-key attack, which exploits the fact that all rounds
are identical and that the key schedule is just a cyclic shift by 32 bits" (p.327)
"...examined Blowfish with known S-boxes..." (p.339) and so on.

        In course of this reading, by an unknown memory twist, I remembered
a funny word combination - "Problem of a Drunken Sailor". Actually this is a 
serious mathematical problem, conventionally known as the problem of random walk.
The essence of this problem can be visualized if one imagines a drunken sailor 
on a street crossing in an unknown city. The guy has about the equal probability 
to go in any of the 4 possible directions. On the next crossing there will be 
again 1 out of 4 choice, then again and again, until he comes to the harbor 
(if this ever happens).

        And then a crazy idea came to my mind - why not introduce some trick 
which will deny the cryptanalyst the access to his main weapon? Why not make the
algorithm itself key-dependent and plaintext-dependent. In this case our friend 
cryptanalyst will be thrown back to square zero, because there will be nothing 
certain to cryptanalyze - only the "C" code which will explain how the choices 
are made between different algorithms.

        Elaborating a little further, the number of possible choices must be 
greater than the combined number of all possible keys and all possible plaintexts.
Then the brute-force attack will really be the only way - all other ways will 
require more plaintexts or more keys than can exist.

        Now the purists are advised to close their eyes and to read the rest of 
the text with the eyes closed, because the main concept which will be discussed 
here is the concept of BOOZE.

        There are two variable parts processed by the encryption algorithm in 
order to produce ciphertext - key and plaintext. Accordingly, if the choices 
in encryption procedure will be dictated by the key alone, we will call this 
Master Booze, if the choices will be dictated by the plaintext, we will call 
this Peer Booze. Both are necessary in varying proportions, and it is my intent 
to draw attention to the fact that a big amount of high quality Peer Booze is
present in many successful block ciphers. Neglect of the amount or quality of 
Booze (both Master and Peer) facilitates the cryptanalysis enormously.

             ---  MASTER BOOZE BREWERY (Stuffing the cellar)  ---

                                         BOOZE (orig. boo-zah) 
                                             * word of Mongolian origin, 
                                               denotes a beverage which makes
                                               people aggressive and delirious.
                                             * (amer.slang) strong liquor.

        Conventionally this procedure is known as Key Schedule, and its purpose
boils down to generating some bytes or words which will be used for subsequent 
encryption. The generated stuff is known as Subkeys, and different ciphers use 
different number of those and different generating procedures. IDEA generates 
its subkeys by the 25-bit cyclic shift of the key, FEAL uses a special "Fk" 
function, BLOWFISH uses itself and the hex digits of Pi, and so on. As a result 
the generated subkeys vary greatly in several respects:

              For some ciphers there exist weak keys which produce subkeys 
          with complementation properties, symmetry, etc.

              Insufficient number of subkeys restricts possibilities for 
          plaintext-dependent choices in the algorithm (little to choose from).

              Not always do all the subkeys depend on every bit of the key, 
          (which is conventionally called the avalanche property).

              Sometimes it is necessary to recompute all the preceding subkeys 
          in order to compute a particular subkey needed at the moment (this 
          can be important in case of memory-restricted applications like 
          smartcards, where you cannot keep all Booze in memory).

        Without elaborating the question in more detail, I am going here to 
propose a procedure with the following properties:

          *   Computationally easy - the only operations used are addition and

          *   Capable of producing an arbitrary number of randomized subkeys.

          *   Change of any bit of the key changes the entire Master Booze 
          array beyond recognition - complete avalanche.

          *   No keys produce the subkeys with symmetry, complementation or other
          undesirable properties - at least to my knowledge. 

          *   Any subkey can be esily recomputed without computing all the 
          previous ones.

        This ease and features come at a price - some additional material is 
necessary for this procedure to work. Fortunately, courtesy of Bruce Schneier, 
we have this required ingredient - the hex digits of Pi, and a reference to 
a Web site, where one can find more.

        The proposed procedure is inspired by the convolution algorithm, which
is very common in digital filtering studies. 

        Essentially the convolution is described by the following formula
              (it is not easy to reproduce it in the ASCII text file)

        If K[n] is the key, and G[i] is some random byte sequence:
      CONV[m] = SUM {G[n+m]*K[n]} ;  where N is the number of points in K[n]

        Obviously, the number of points in G[i] must be big enough to allow 
this procedure. The file with the hex digits of Pi referenced by B. Schneier 
has 65536 hex digits, which should be sufficient for any realistic purpose.

        In practice, the algorithm works equally well for bytes, short or 
long integers, the random sequence just does not care.

        Both cipher packages include a "C" program which implements the algorithm 
in byte version. Multiplication is unsigned Mod 255, all eventual carries and 
overflows are disregarded (anyway, this is meant to be a one-way procedure). 
For purists who don't like the mentioning of Booze, I have an alternative name 
for this program - PUKS, which stands for "Proposed Universal Key Scheduler".

        Having computed as many subkeys as desired, we can now proceed 
further and develop some more sophisticated beverages. These will provide 
vast opportunities for some parts of the plaintext to present random choices 
to another parts, choices dependent on the plaintext itself and on the 
assortment of strong Booze in the cellar.

             --- Taking them to the Saloon (two case studies) ---

                                              Twelve drunken men
                                                    On the chest of the dead...
                                                    And a bottle of rum!

        In order to appreciate the difference between a sober cipher and a 
drunken one, I decided to take two well known ciphers as guinea pigs and pour
a good amount of Booze down their throat. Again thanks to the courtesy of Mr.
Schneier the choice was obvious - FEAL-8, abandoned even by its creators in
favor of FEAL-NX, and subject to all kinds of cryptoanalysis, and MMB, which
Bruce Schneier declared simply "dead". It was interesting to see, could these
two "corpses" be revitalized by offering them a good drink. The source code
is easily found on the WWW, as well as digits of Pi and other random stuff.

Case study 1:  FEAL-8

        This is a classical Feistel cipher with 64-bit block and 8 rounds of
encryption. Even the superficial study of the encryption procedure reveals
an important shortcoming - this cipher used only Master Booze, the subkeys for
consecutive rounds were predetermined, and although each round used different
subkeys, the plaintext peers had no voice at all in this process, all the 
sequence was determined by Master. This was wrong, so the changes were to happen
precisely at this level.

        The heart of the change is in one single line of code in the routine
for the evaluation of "f" function:

WAS:       B.All = BB ;    Subkey provided by external routine

IS NOW:    B.All = K[A.Byte[0]] ^ K[A.Byte[1]] ^ K[A.Byte[2]] ^ K[A.Byte[3]];

                           Subkey selected from an array, using plaintext
                           as a source of indices.

        Well, in order to make this one change possible, several other changes
had to be done as well.

        Obviously, since the indices are 8 bits long, the array of subkeys must
have at least 256 elements (practically it has 264, the extra 8 used in the 
initial and final XOR-ing). The subkeys used in FEAL-8 are 16 bits long, so 
this means that 528 bytes of Booze needed to be generated.

        It made sense to generate all the Booze in the setup procedure, the
array dimensions were chosen to accomodate the increased number of entries.
Subkeys K[0] to K[255] are used in the rounds, subkeys K[256] to K[263], as
mentioned, are used for XOR-ing. The "SetKey" routine takes over after the 
"MasterBooze" routine, uses the generated subkey bytes and "stuffs the cellar".
The "FK" function from the original FEAL-8 is not needed anymore, so it was

        The revamped cipher was compiled and tested (very superficially, I am
not a pro in cipher testing). The change of any bit in the key changes all the
subkeys, that is natural, it could not be otherwise.

        Change of one bit in the plaintext produces an avalanche-like change
in the ciphertext starting from the 2-nd round, and every 2 rounds thereafter.
It is verified that all bits of the plaintext have a fair and equal chance to
vote in the Peer Booze selection - they choose their Peer Booze out of 2^16
possible variants, give or take a dozen. 

        Now we can make a quick estimate - how many choices does the cipher
present to a cryptanalyst. Sure, this is not the clear-cut case of random
algorithm, only parameters are random, but this is already something that
is worth estimating.

        First, there is initial and final XOR-ing with unknown 64-bit numbers
(remember the 16 extra subkey bytes). This gives 128 bits of Master Booze,
already a significant amount of confusion. Then the 8 rounds with 16-bit
subkeys contribute additional 128 bits of Peer Booze. In my humble view, the
total amount of Booze consumed in this cipher is close to 256 bits, so the 
only way of breaking it will be the brute force guessing of the plaintext.
Remember that the key can be up to 64 bytes long (512 bits), and it has more
security than the Master Booze and Peer Booze combined. Brute force cracking
the key is out of question.

Case study 2:  MMB

        This was a far more demanding customer, and it provided opportunity for
some fine points which I hope can be used in other areas beyond this case study.

        First of all, even if classical MMB is "dead", there is already a new 
MMB-20 with "Strengthened Key Schedule", and this is what I took for a starting
point (just to discard all this Key Schedule in favor of MasterBooze a.k.a. PUKS)
Nevertheless, this version had a neat routine for Modular Multiplication, and 
that alone justified the choice. Constants defined in the program also turned 
out to be quite useful.

        Before we turn on to the cipher itself, a couple of facts about modular 
multiplication need to be reminded.

         Suppose we have a number relatively prime to N which for our purposes
we shall call SEED. Then two numbers:

           SEED^k mod N     and     SEED^(T(N) - k) mod N

                                  ( where T(N) is Euler totient function )

are in fact the multiplicative inverses of each other. So for any random "k" 
it is possible to compute a corresponding multiplier-inverse pair. BINGO !!

        This called for some experimentation. I wrote a short program which
took the value of SEED from input, then calculated the subsequent powers
of SEED mod (2^32 - 1), and counted the number of iterations until the result 
was equal to 1. Some rudimentary care was taken to assure that SEED and 
(2^32 - 1) would be relatively prime. (For those interested in the source 
code, this program was written in FORTH, and I cannot sing enough praise to 
the interactivity and ease of programming in this miraculous language). 

      Two important facts were discovered.

      * The maximum number of iterations needed to come down to 1 turned out
to be equal to 2^16 (65536). Remaining on the realistic side, I was very happy 
with this result. It meant that for an appropriately chosen SEED there exist 2^16 
different numbers which can be generated by raising SEED to a random power 
"k" < 2^16. Each of these numbers has a multiplicative inverse which in turn 
can be generated by raising SEED to the power (2^16 - k).

      * Two of these multipliers are trivial - they are themselves their 
own multiplicative inverses. 
        These are SEED^(2^16) = 1 and SEED^(2^15) = 0x10000, so care should 
be taken in order to avoid using them in the encryption program. Readers are
welcome to look into the MMBOOZE source code and look into "Make_2_Multi" 
routine to find out the exact procedure.

        A number chosen to be the SEED is equal to 0x2F8E6D85, this was chosen 
for no particular reason other than the fact that it generates a 2^16 long 
cycle of its subsequent powers. 

        Actually the Make_2_Multi routine generates two numbers at once - the 
multiplier and its inverse, both are generated within the same loop and routed
to the adjacent cells in the "Mult" array. The total of 128 multipliers and 
128 inverses are generated based on subkey bytes from "ss[128]" to "ss[383]".

        The encryption process consists of 3 rounds of modular multiplication 
with initial, two intermediate and final XOR-ing of the text with the Master
Booze bytes. Since the maximum length of the plaintext can be up to 32 bytes,
128 bytes of Master Booze are generated for this purpose. Some of them may
stay unused in case of shorter plaintext blocks.

        The modular multiplication round differs significantly from the one 
used in the original MMB-20 routine in two respects. First, when 4 bytes
are taken in order to form the 32-bit number, the multiplier is chosen based 
on the preceding two bytes in the plaintext sequence, just by XOR-ing these
bytes together. This makes the choice of the multiplier plaintext-dependent 
and gives all bits in the plaintext a fair and equal chance of voting. 
Second, after the multiplication, when the bytes of the product are returned 
to their places and it is time to proceed with the subsequent multiplication, 
the two MS bytes of the previous product provide the number for the choice 
of subsequent multiplier, the two LS bytes of the previous product become the 
two MS bytes of the new number to be multiplied, and two adjacent bytes of 
the plaintext are appended to them in order to make this new 32-bit number.

        Graphically a round can be visualized in the following sketch:
          ( the array index is circular mod "ArrayLength" )

                  1-st multiplication


        ( XXXX - 32-bit number to multiply, MM - bytes for multiplier choice)

                  2-nd multiplication


        ( PPXX - 32-bit number to multiply, MM - bytes for multiplier choice)

                  3-d multiplication


        ( PPXX - 32-bit number to multiply, MM - bytes for multiplier choice)

                  Last multiplication


        ( The index is cyclically wrapped over -
          PPXX - 32-bit number to multiply, MM - bytes for multiplier choice)

        This arrangement allows an ultra-rapid propagation of all changes 
across the entire plaintext - there is a complete avalanche already after
the 2-nd round, and in my humble opinion, 3-d round is close to an overkill,
taking into account all the intermediate XOR-ings. However, I decided to let 
it stay, mainly because the overlapping doubles the number of multiplications 
per round, so the total number of multiplications is the same as in original 
MMB-20 cipher in 6 rounds without overlapping.

        It should be also noted that this arrangement effectively molds the 
entire plaintext-ciphertext array into an inseparable entity, where the 
junctions between the adjacent bytes and words don't exist any more, they are 
all blurred by overlapping and by cyclical wrapover of the index. This 
strongly discourages the attacks based on different behavior of the distinct
bytes and words during encryption. In this cipher the entire ciphertext is 
just a circular sequence of bits without beginning or end, without the 
junctions between bytes or words, without any realistic possibility to trace 
the output changes back to the changes in any particular word or byte. The 
cryptanalyst would either be forced to attack the ciphertext as a whole 
(128 or even 256 bits), or just give up, which I hope will precisely happen.

        Decryption is the same as encryption, the XOR-ing Master Booze is just
used in reverse order, during the multiplication the index rolls backward, and 
in the number for multiplier choice the LSB is flipped over, so that instead 
of multiplier used for encryption, its inverse is selected from the adjacent
cell. If multiplier #94 was used in the encryption, multiplier #95 will be 
selected for decryption, and vice versa.

        I must confess that I shamelessly borrowed from FEAL two neat routines, 
one for assembling the 32-bit number from 4 bytes, another one for breaking 
the 32-bit number into 4 bytes. I just wanted the program to work identically 
on all kinds of machines - big-endian and little-endian alike. This slows the 
computations quite a bit, but anyway, this was meant to be a model, not a 
racing car. Those who will have the drive and desire to build MMBOOZE into 
their products, are welcome to write the Assembler routines for modular 
multiplication, revise the byte positioning in memory, roll the indices 
in reverse order, generate the Master Booze from higher indices towards 
the lower ones, in short, do all kinds of amendments in order to speed up 
the execution. 

             ---   EPILOGUE...  AES submission, etc.  ---

        After this essay was completed, I thought that MMBOOZE would make a 
worthy submission to the AES competition, but on the second thought I stopped 
regretting. Imagine a completely drunken cipher among the bunch of its sober 
colleagues, you get the idea? Besides, AES submission required a voluminous 
pseudoscientific description both in Acrobat and in Postscript formats, some 
reference and not-so-reference implementations, strict adherence to NIST 
guidelines, inclusion of NIST headers, estimates of performance on different 
platforms, etc, etc. This was certainly not a job for an individual amateur 
with an old 486/100 computer and 14400 link to the Net. And I was simply late, 
the AES submissions were ended when the MMBOOZE model made its first steps.

        To end this all on an optimistic note:

                  I leave you drunken MMBOOZE,
                  Which everyone is free to use...

                  If you find there a trapdoor,
                  Push it! Don't ask me any more !

        With best regards                         Boris Kazak.