Cryptography-Digest Digest #695, Volume #9       Fri, 11 Jun 99 03:13:02 EDT

Contents:
  Re: IDEA-128 (Boris Kazak)
  Re: ATTN: Bruce Schneier - Street Performer Protocol ([EMAIL PROTECTED])

----------------------------------------------------------------------------

From: Boris Kazak <[EMAIL PROTECTED]>
Subject: Re: IDEA-128
Date: Thu, 10 Jun 1999 23:22:25 -0400
Reply-To: [EMAIL PROTECTED]

[EMAIL PROTECTED] wrote:
> 
> In article <[EMAIL PROTECTED]>,
>   Casey Sybrandy <[EMAIL PROTECTED]> wrote:
> 
> > b0 = rotr(b0, r[j++])
> > b1 += k[i++]
> > b2 += k[i++]
> > b3 = rotr(b3, r[j++])
> 
> Bad idea, only two blocks are permutated and the others are just
> manipulated.  I.e there are only 2^5 or 32 possible modifications of b0
> and b3 in the input, but 2^32 possible modifications of b1 and b2.
> (**************)
> (**************)
> Well sorry but it doesn't look that good.  I pointed out some things,
> but here is more.  The multiplication runs in one direction only, this
> means no matter how much rotating you perform there is some set of weak
> bits in each round.  I don't know how to exploit this myself, but it
> probably could be done.  The fact that b0/b3 are not permutated
> properly is bad as well.  The keyed rotation could help linear
> analysis, however I think some chars can be found (such as with the
> parity bit and the lsb).  This is because multiplication in the GF
> (2^32) (32bit result) is not bijective, which means not all outputs are
> possible (take for example the LSB which is 0 75% of the time).
> 
> BTW do you know why IDEA uses multiplication?  I think you should look
> at it a little more first.  The cipher is not that bad, but needs some
> work arounds to become secure.
> 
> Tom
> ---------------------


If you are interested with 128-bit block size IDEA, here is a working 
model which is much closer to original design, it uses multiplication 
(mod 2^32+1).
*************************************************************
                      "A naive variation might double the block size.
                       The algorithm (IDEA) would work just as well with
                       32-bit sub-blocks instead of 16-bit sub-blocks
and
                       a 256-bit key. Encryption would be quicker and 
                       security would increase 2^32 times. Or would it?
                       The theory behind the algorithm hinges on the
fact
                       that 2^16 + 1 is a prime, 2^32 + 1 is not.
Perhaps
                       the algorithm could be modified to work, but it 
                       would have very different security properties.
                       Lai says it would be difficult to make it
work..."
                       (B. Schneier "Applied Cryptography" 2-nd ed.
p.325)

     Let's say that I was naive...
**************************************************************
     From the point of view of encryption modular multiplication is just 
equivalent to the S-box with the corresponding number of entries. It
maps 
the input subblock to the output subblock of the same size (16 or 32
bits).
The inverse transformation must be always possible, otherwise the
subblock
will become undecryptable. 
     However, 2^32 + 1 is a composite number, equal to 641 x 6700417,
and 
there are 32-bit numbers which have no multiplicative inverses
(multiples
of 641 or of 6700417). There are not a lot of numbers like this, but
they
exist, and the algorithm must have an automatic way to avoid those.
     The situation has another aspect. In 8 full rounds of IDEA only 34 
multipliers are used for encryption. Thus if it would be possible to
come 
up with the way to set up 34 key-derived long multipliers and 34
inverses,
the algorithm would work with the double block length as easy as with
the 
original one.

     The premises were as follows. All operations (mod 2^32 + 1).

        1. If a 32-bit number is repeatedly multiplied by itself,
yielding
     consecutive powers (mod 2^32 + 1), this process cannot go forever.
     (The number must be relatively prime to 2^32 + 1). After a certain
     number of multiplications the product will become equal to 1, and 
     then the whole cycle will repeat. This is a 200-year old fact, due 
     to Euler, and the maximum possible length of the loop is equal to
the
     least common multiple of (641 - 1) and (6700417 - 1). This common
     multiple is 33502080.

        2. The actual length of the loop can be a fraction of the
maximum
     possible, depending on the initial starting number (in future
discussion 
     this number will be called SEED).

        3. If the length of the loop is N, then SEED^N = 1. Also it
follows
     that SEED^(N-1) * SEED = 1, consequently SEED^(N-1) and SEED are 
     multiplicative inverses of one another, same is true for SEED^(N-2)
and
     SEED^2, for SEED^(N-3) and SEED^3, and so on. Interesting enough,
     SEED^(N/2) is its own multiplicative inverse, just like 1.

        4. The necessary multipliers can be set up as powers of an
     appropriately chosen SEED, where the 16-bit subkeys will determine
the
     power to raise the SEED. Inverses can be set up in the same way.

     Finding the necessary numbers turned out to be easy. I stopped
after
finding 4 appropriate values for SEED, although probably could find a
dozen 
more without effort. Here are the numbers and inverses.

                    SEED                    INVERSE
                   0x6A5F92D5             0xB28409BC
                   0x25E6D746             0xC9868267
                   0x8B34DD6A             0x3EB4FD73
                   0x6A09E667             0x4520A9C6
(this last number pair is especially interesting. The SEED happens to be
the first 8 hex digits of SQRT(2), less initial 1, the use of this
number
will dispel any suspicions about a "trap door" built into SEED).

     From now on everything was clear. Each of the above numbers
produces a 
33502080 long cycle of its powers, the same is true for inverses. It is 
sufficient to take the 16-bit IDEA subkey, raise the SEED to the
corresponding
power, raise the INVERSE to the same power, and "lo and behold!", here
are
two fully functional modular multipliers, derived from the key and ready
to be
used for encryption and decryption.
     Practically it turned out that computations could be done very fast
if 
"fundamental" powers of SEED and  INVERSE were precomputed and arranged 
into a table.

     The algorithm works on the 128-bit block divided into 4 32-bit
subblocks.
The sequence of operations is the same as in the original IDEA, the
number of
rounds can be made variable by redefining the constant ROUNDS.
     Key scheduling is different from original IDEA. It uses the same
modular 
multiplication and the same SEED to produce a randomized stream of
subkeys. This 
program will accept any length of key up to 64 bytes, in the posted
version the 
constant MAXKEY is #defined as 7, limiting the length of the user key to
7 
characters (56 bytes), in order to comply with the USA export
restrictions.

     Any ANSI-compliant version of C will compile this program. The
short 
"main" is used for illustration of the program functioning.
     No attempt was made to optimize the program in any way. It is
intended to
serve as a working model, not as a racing car.

/********************** Start of code *****************************/
/* idea_128.h */


#include <stdio.h>
#include <time.h>
#include <process.h>
#include <io.h>
#include <string.h>
#include <conio.h>

#define IDEABLOCKSIZE 16
#define byte unsigned char
#define word16 unsigned int
#define word32 unsigned long int
#define ROUNDS  8
#define MAXKEY 7 /* Compliance with 56-bit export requirements */
#define SUBKEYBYTES     (16*ROUNDS+12)
#define LO 0x0000FFFFUL
#define HI 0xFFFF0000UL
#define SEED  0x6A09E667UL  /* Modular Multiplier seed */
                       /* First 8 hex digits of SQRT(2), less initial 1 */
#define INVERSE 0x4520A9C6UL /* corresponding inverse MOD 2^32+1 */
                                  /* Powers of SEED and INVERSE will be used */
#define low16(x) ((x) & 0xffff)
#define DEBUG
/*IDEA Algorithm functions */
void MasterBooze ( int );
void Idea_set_key (void);
void cipher_idea(word32 in[],word32 out[],word32 subkey[]);
word32 Mul(word32 a,word32 b);

  /*global vars*/

word32 enckey[6*ROUNDS+4],deckey[6*ROUNDS+4]; /* Actual IDEA_128 subkeys
*/
byte key[64];   /* This will hold the user key. */
byte ss[SUBKEYBYTES];   /* This will hold the subkey bytes */

/******************************************************/

/* IDEA_128.C
        C source code for IDEA_128 block cipher.
        IDEA (International Data Encryption Algorithm),
        formerly known as IPES (Improved Proposed       Encryption Standard).
        Algorithm developed by Xuejia Lai and James L. Massey, of ETH Zurich.
        This implementation modified and derived from original C code
        developed by Xuejia Lai. Zero-based indexing added, key scheduling
        uses the same modular multiplication algorithm mod(2^32+1).
        Plaintext bytes and subkey bytes are rearranged so that result must be
        independent of the native byte ordering of the processor.

                The main() used here as illustration reads the key
                from the input string, and encrypts-decrypts two plaintext
                blocks. The first block consists of all 0-s, the second
                block has 1 in the 4-th plaintext byte.
                Plaintext, ciphertext, and decrypted plaintext are shown
                on screen if DEBUG is #defined.
                Also shown are the user key with padding up to 64 bytes
                and the hash of the key, which is subsequently used for the
                derivation of subkey bytes.

        This code is explicitly and irrevocably placed into public domain
        starting from January, 20, 1999 */


#include "idea_128.h"

static word32 Mul_Power[16] =
{
  0x6A09E667UL, 0x22AF0A42UL, 0x89F2429CUL, 0xADEF9E84UL,
  0x4E717789UL, 0x22B9502FUL, 0x7455A930UL, 0x14D2AB03UL,
  0x797769F4UL, 0x1B0FF6B4UL, 0xCBFA0F88UL, 0x640AAF5DUL,
  0xAC4BEE57UL, 0xF8E9C2C8UL, 0xB23DFD6AUL, 0xAB446C7BUL
}; /* SEED^1, SEED^2, SEED^4,...,SEED^(2^15), all MOD(2^32 + 1) */

static word32 Inv_Power[16] =
{
  0x4520A9C6UL, 0x8B6C6575UL, 0x84FEA367UL, 0x9B98BDA7UL,
  0x9E3DB228UL, 0x613B6F79UL, 0x1F620EEBUL, 0x52F1A68DUL,
  0x3AF576DCUL, 0x5AB367D8UL, 0x220CE382UL, 0xA1D860F0UL,
  0x3363002FUL, 0xD4097046UL, 0x5689B0D8UL, 0xC418E961UL
}; /* INVERSE^1, INVERSE^2,...,INVERSE^(2^15), all MOD(2^32 + 1) */

word32 Mul (word32 x, word32 y)
        /* Returns (x * y) mod (2^32+1)
                For the purposes of this algorithm 0 = 2^32 */
        /* Modular Multiplication "CORRECT BUT SLOW" mod 2^32+1
                If your compiler does not handle the "double long"
                integers (64 bit), you have to split the 32-bit words
                into 16-bit halves, multiply them, then juggle the
                intermediate products until you get the correct result.
        */
{
        word32 t[6]; word16 u[2], v[2];
                if (x == 0) return (1 - y);
                if (y == 0) return (1 - x);

        u[0] = x & LO;           v[0] = y & LO;
        u[1] = (x >> 16) & LO;  v[1] = (y >> 16) & LO;

        t[0] = (word32) u[0] *  (word32) v[0] ;
        t[1] = (word32) u[1] *  (word32) v[0] ;
        t[2] = (word32) u[0] *  (word32) v[1] ;
        t[3] = (word32) u[1] *  (word32) v[1] ;
                          /* intermediate 32-bit products */

        t[3] = t[3] + ((t[1] >> 16) & LO)
                                        + ((t[2] >> 16) & LO);
                                        /* no overflow possible here */

        t[1] = (t[1] & LO) + (t[2] & LO)
                                        + ((t[0] >> 16) & LO);
                                        /* collect them all in t[1]. Overflow into the
                                                17-th bit possible here */

        t[0] = (t[0] & LO) + ((t[1] << 16) & HI);
        t[3] = t[3] + ((t[1] >> 16) & LO); /* add eventual overflow */

        return (t[0] - t[3] + (t[3] > t[0]));

} /* Mul */

#define MUL(x,y) (x=Mul(x,y))

void Make_2_Multi ( word16 source, word32 *mul, word32 *inv )
{
  word16 mulpower;
  int k;

  mulpower = source;  /* This is the integer power to which SEED
                                                                 and INVERSE are 
raised */

                  *mul = SEED; *inv = INVERSE;
                  /* Initialize multiplier and inverse */
                  for (k = 0; k < 16; k++)
                         {
                         if ((mulpower & 0x0001) == 1)
                          {    *mul = Mul(*mul, Mul_Power[k]);
                                 *inv = Mul(*inv, Inv_Power[k]);
                          } /* Contribute to the multipliers */

                         mulpower = mulpower/2;  /* Get the next bit of source */
                         }
} /* Make_2_Multi */

void DissH1( word32 H, byte *D )
/*
          Disassemble the given word32 into 4 bytes.
          We want it to work on all kinds of machines,
          Little-Endian or Big-Endian.
*/
{
          word32 T ;

         T = H ;
         *D++ = ((T & 0xff000000UL) >> 24) ;
         *D++ = ((T & 0x00ff0000UL) >> 16) ;
         *D++ = ((T & 0x0000ff00UL) >>  8) ;
         *D   =  (T & 0x000000ffUL) ;
}

word32 MakeH1( byte *B )
/*
          Assemble a word32 from the four bytes provided.
          We want it to work on all kinds of machines,
          Little-Endian or Big-Endian.
*/
{
          word32 RetVal, temp ;

         temp = *B++ ;
         RetVal = (temp << 24) ;
         temp = *B++ ;
         RetVal += (temp << 16) ;
         temp = *B++ ;
         RetVal += (temp <<  8) ;
         RetVal += *B ;
         return RetVal ;
}

void Idea_set_key (void)
{
  int i, source;   /* "source" is the power to raise the SEED */
  word32 temp;  /* this will keep the unneeded multiplicative inverse */

         for (i = 0; i <= ROUNDS; i++)
         {
                                source = (word16) ss[16*i]*256 + (word16) ss[16*i + 1];
                                Make_2_Multi(source, (enckey+6*i), 
(deckey+6*(ROUNDS-i)));
                                                                         /* set up mul 
and inv */
                                enckey[6*i + 1] = MakeH1(ss+16*i + 2);
                                enckey[6*i + 2] = MakeH1(ss+16*i + 6);
                                                                         /* set up 2 
additives */
                        if((i > 0) && (i < ROUNDS))
                          {
                                deckey[6*(ROUNDS - i) + 1] = -enckey[6*i + 2];
                                deckey[6*(ROUNDS - i) + 2] = -enckey[6*i + 1];
                          }               /* flip the order of 2 inverses for inner 
rounds */
                        else
                          {
                                deckey[6*(ROUNDS - i) + 1] = -enckey[6*i + 1];
                                deckey[6*(ROUNDS - i) + 2] = -enckey[6*i + 2];
                          }        /* straight order for 1-st and last round */

                        source = (word16) ss[16*i + 10]*256 + (word16) ss[16*i + 11];
                        Make_2_Multi(source, (enckey+6*i+3), (deckey+6*(ROUNDS-i)+3));
                                                                                       
                                  /* set up mul and inv */
                        if (i < ROUNDS)
                          {  /* These 2 multipliers are not needed for the last round 
*/
                                source = (word16) ss[16*i + 12]*256 + (word16) ss[16*i 
+ 13];
                                Make_2_Multi(source, (enckey+6*i+4), &temp);
                                source = (word16) ss[16*i + 14]*256 + (word16) ss[16*i 
+ 15];
                                Make_2_Multi(source, (enckey+6*i+5), &temp);
                                        /* last 2 muls are used directly        
without inversion */
                                deckey[6*(ROUNDS-i-1) + 4] = enckey[6*i + 4];
                                deckey[6*(ROUNDS-i-1) + 5] = enckey[6*i + 5];
                          }
         }
}

static void cipher_idea(word32 in[4],word32 out[4],word32 subkey[])
{
        word32 x1,x2,x3,x4,t1,t2;
        int r=ROUNDS;
        x1=*in++; x2=*in++;
        x3=*in++; x4=*in;
        do
        {
                MUL(x1,*subkey++);
                x2+=*subkey++;
                x3+=*subkey++;
                MUL(x4,*subkey++);
                t2=x1^x3;
                MUL(t2,*subkey++);
                t1=t2+(x2^x4);
                MUL(t1,*subkey++);
                t2=t1+t2;
                x1^=t1;
                x4^=t2;
                t2^=x2;
                x2=x3^t1;
                x3=t2;
        } while (--r);
        MUL(x1,*subkey++);
        *out++=x1;
        *out++=(x3+*subkey++);
        *out++=(x2+*subkey++);
        MUL(x4,*subkey);
        *out=x4;
}

void Idea_Encrypt (byte pt[], byte ct[])
{
        int i;
        word32 text[4];
        for (i = 0; i < 4 ; i++) text[i] = MakeH1(pt + 4*i);
                          /* Assemble 4 32-bit words for encryption */
#ifdef DEBUG
                printf("plain=  %08lx %08lx %08lx %08lx
\n",text[0],text[1],text[2],text[3]);
#endif /* DEBUG */
        cipher_idea ( text, text, enckey);
#ifdef DEBUG
                printf("cipher= %08lx %08lx %08lx %08lx
\n",text[0],text[1],text[2],text[3]);
#endif /* DEBUG */
        for (i = 0; i < 4 ; i++) DissH1(text[i], (ct + 4*i));
                          /* Forward encrypted bytes to where they belong */
}

void Idea_Decrypt (byte ct[], byte pt[])
{
        int i;
        word32 text[4];
        for (i = 0; i < 4 ; i++) text[i] = MakeH1(ct + 4*i);
                          /* Assemble 4 32-bit words for decryption */
#ifdef DEBUG
                printf("cipher= %08lx %08lx %08lx %08lx
\n",text[0],text[1],text[2],text[3]);
#endif /* DEBUG */
        cipher_idea ( text, text, deckey);
#ifdef DEBUG
                printf("plain=  %08lx %08lx %08lx %08lx
\n",text[0],text[1],text[2],text[3]);
#endif /* DEBUG */
        for (i = 0; i < 4 ; i++) DissH1(text[i], (pt + 4*i));
                          /* Forward decrypted bytes to where they belong */
}

/*      Here is the procedure for Subkey generation.
        It uses the same modular multiplication mod(2^32+1) and
        the same  SEED = 0x6A09E667 in order to generate necessary
        subkey bytes.
        First cycle of modular multiplication is needed to compute the
        "hash" of the key together with padding, this makes every one
        of the subkeys dependent of every bit of the key.
        During the computation of subkeys, index is circular mod 64.

        Variables:
        key[64] -  unsigned character array, contains the user key.
                                  (Compiler grumbles about "mixing pointers to
                                  different CHAR types" - disregard)

        ff[64] -   function F[n], contains the user key and padding
                        (if the external user key is shorter than 64 bytes,
                        it is padded with bytes 0x01, 0x02, 0x03 and so on,
                        as many as needed to make the total equal to 64 bytes)

        ss[SUBKEYBYTES] -  unsigned character array, contains subkeys
                        (size of this array is at the discretion of the
                        programmer, constant SUBKEYBYTES must be defined somewhere)
*/
void MasterBooze ( l ) /* This subroutine needs the length of the
keystring, */
  /*  it uses the global variables key[] and ss[] for input and output
*/

{
int k,m;
word32 temp1, temp2;
unsigned char ff[64];  /* This will contain the key */

        for (m=0; m < l; m++) ff[m] = key[m];  /* copying the user key until */
                                                /* "l" bytes are copied */
        for (k=0; (m+k)<64; k++) ff[m+k] = k+1; /* padding the key with
1,2,3... */
#ifdef DEBUG
         printf("\nkey  =");
         for(k=0; k<64 ; k++)
          {
                 printf("%2x",ff[k]);
                 if(k==31) printf("\n      ");
          }
#endif   /* DEBUG */
                                                                  /*  computing key 
hash */

        for (k=0; k<64; k+= 2)
 {
         temp2 = ff[k % 64] ;        /* Essenntially this procedure */
         temp1 = (temp2 << 24) ;    /* is identical to MakeH2 function */
         temp2 = ff[(k+1) % 64] ;   /* However, it is impossible to use */
         temp1 += (temp2 << 16) ;   /* MakeH2 function directly, because */
         temp2 = ff[(k+2) % 64] ;   /* the index must be wrapped mod 64, */
         temp1 += (temp2 <<  8) ;   /* which is not provided in MakeH2 */
         temp1 += ff[(k+3) % 64] ;

         MUL(temp1, SEED);    /* Get the modular product into "temp1" */

         ff[k % 64] = ((temp1 & 0xff000000UL) >> 24) ;
         ff[(k+1) % 64] = ((temp1 & 0x00ff0000UL) >> 16) ;
         ff[(k+2) % 64] = ((temp1 & 0x0000ff00UL) >>  8) ;
         ff[(k+3) % 64] =  (temp1 & 0x000000ffUL) ;
                                                         /* Return the bytes where 
they belong */
 }
#ifdef DEBUG
          printf("\nhash =");
          for (k=0; k<64; k++)
         {
                printf("%02x", ff[k]);
        if(k==31) printf("\n      ");
         }
          printf("\n");
#endif

                 /* Now that the hashed key is computed, the same routine
                 is used to produce the actual subkey bytes  */

        for (k=0; k<SUBKEYBYTES; k+= 2)
 {
         temp2 = ff[k % 64] ;        /* Essenntially this procedure */
         temp1 = (temp2 << 24) ;    /* is identical to MakeH2 function */
         temp2 = ff[(k+1) % 64] ;   /* However, it is impossible to use */
         temp1 += (temp2 << 16) ;   /* MakeH2 function directly, because */
         temp2 = ff[(k+2) % 64] ;   /* the index must be wrapped mod 64, */
         temp1 += (temp2 <<  8) ;   /* which is not provided in MakeH2 */
         temp1 += ff[(k+3) % 64] ;

         MUL(temp1, SEED);    /* Get the modular product into "temp1" */

         ff[k % 64] = ((temp1 & 0xff000000UL) >> 24) ;
         ff[(k+1) % 64] = ((temp1 & 0x00ff0000UL) >> 16) ;
         ff[(k+2) % 64] = ((temp1 & 0x0000ff00UL) >>  8) ;
         ff[(k+3) % 64] =  (temp1 & 0x000000ffUL) ;
                                                         /* Return the bytes where 
they belong */
         ss[k] = ff[k % 64];                     /* subkey byte */
         ss[k + 1] = ff[(k+1) % 64];             /* one more subkey byte */
 }
}    /* MasterBooze */



void main ()
{
        int k;
        byte pt[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, ct[16];
                                                                 /* plaintext and 
ciphertext */
                printf ("\n  Enter your password:");
                gets(key);
                k = strlen(key);

                if (k > MAXKEY)
                {
                        printf("\n The key contains more than %i characters.",MAXKEY);
                        printf("\n Excess characters will be truncated.");
                        k = MAXKEY; key[k] = 0;
                }  /* Compliance with export requirements */

                MasterBooze( k );


        /* These lines are for testing "idea_128" */
                 Idea_set_key ();
                 printf ("\n");
                 Idea_Encrypt ( pt, ct );
                 Idea_Decrypt ( ct, pt );
                 pt[4] = 1;
                 printf ("\n");
                 Idea_Encrypt ( pt, ct );
                 Idea_Decrypt ( ct, pt );
}  /*  main */

------------------------------

From: [EMAIL PROTECTED]
Subject: Re: ATTN: Bruce Schneier - Street Performer Protocol
Date: Thu, 10 Jun 1999 14:09:34 GMT

In article <[EMAIL PROTECTED]>,
  Mok-Kong Shen <[EMAIL PROTECTED]> wrote:

> You launched a bunch of very severe attacks agains Mr. Schneier,
> however without giving any reference to his writing. I just looked
> at the web page of counterpane without finding anything remotely
> related to the target of your attack. This is non-scientific way of
> argumentation and is not appropriate for this group. Please redo
> your work, give pointer to references and say precisely what you think
> is wrong and give concise reasons for that. That you write anonymously
> is perfectly o.k.

The street performer page is avail on his site.  I got it from their.
If you cannot find it I could email it from home later.  I still think
mr 'unknown' should have shut his lip.

Tom
--
PGP public keys.  SPARE key is for daily work, WORK key is for
published work.  The spare is at
'http://members.tripod.com/~tomstdenis/key_s.pgp'.  Work key is at
'http://members.tripod.com/~tomstdenis/key.pgp'.  Try SPARE first!


Sent via Deja.com http://www.deja.com/
Share what you know. Learn what you don't.

------------------------------


** FOR YOUR REFERENCE **

The service address, to which questions about the list itself and requests
to be added to or deleted from it should be directed, is:

    Internet: [EMAIL PROTECTED]

You can send mail to the entire list (and sci.crypt) via:

    Internet: [EMAIL PROTECTED]

End of Cryptography-Digest Digest
******************************

Reply via email to