Cryptography-Digest Digest #819, Volume #11      Fri, 19 May 00 08:13:01 EDT

Contents:
  Cipher Contest: Whirl128 (paper) (Runu Knips)

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

Date: Fri, 19 May 2000 13:57:09 +0200
From: Runu Knips <[EMAIL PROTECTED]>
Subject: Cipher Contest: Whirl128 (paper)


/* vim:ts=4:sw=4

                 WHIRL128 - A cipher algorithm
                 Thu May 18 2000 by Runu Knips

Whirl128, or short Whirl, is a block cipher, using 128 bit blocks.
Its key schedule is created by an avalanche addition. The encryption
and decryption routines use a 14 double round long Feistel loop,
together with input and output whitening and a number of key and
data dependend transformations. The maximum size of the key is 2048
bits, and you shouldn't use it with less than 128 bit.

The algorithm is FREE and as far as I know (which might not be too
much) it is also not covered by any patents.

The basic concepts behind Whirl where the following. First, I didn't
wanted to use s-boxes, which have excellent properties for crypto,
but require masses of static or even dynamic memory. I wanted to get
non-linearity using simple arithmetic operations instead. Second, I
wanted to 'whirl' the bits, i.e. each of the four 32 bit inputs is
processed in a slightly different way. This way I wanted to increase
the nonlinearity of the algorithm.

The performance of the portable C implementation below is arround
1.5 MB/s on my 300 MHz K6-2 (egcs-2.91.66, Linux 2.2 kernel). The
key schedule needed less than 0.1 ms on the same machine. I would
state that this is near the lowest end of acceptable speed for an
unoptimized version of a cipher.

Technically I have to add that all input and output data, which are
of course byte streams, is always loaded as 32 bit values in network
order, i.e. treated as big endian, with the most significant byte
first.


1. Key Schedule
===============
Whirl uses 2048 bit (256 byte, or 64 32 bit values) of round and
whitening data. It is gained with a four round avalanche addition.
But first we initialize the array of key schedule data K with the
user specified key D.

*------------------------------------------------------------------*
| for i in [0..63] do                                              |
|   K[i] := 0;                                                     |
| for i in [0..min(sizeof(D),sizeof(K))-1] do                      |
|   K[i] := D[i];                                                  |
*------------------------------------------------------------------*

For the avalanche effect, we add to each schedule value a number of
other values with fixed relative offsets to the current offset. To
increase the effect very substantly, we also rotate the values
before we actually add them. Without this rotation, all bits would
be unable to influence any other bits in lower significant
positions.

*------------------------------------------------------------------*
| for j in [1..4] do                                               |
|   for i in [0..63] do                                            |
|     K[i] := K[i] + 0xb7e15161                                    |
|       + (K[(i+ 3) mod 64] <<< K[i] shr 10)                       |
|       + (K[(i+13) mod 64] <<< K[i] shr 25)                       |
|       + (K[(i+32) mod 64] <<< K[i] shr  0)                       |
|       + (K[(i+41) mod 64] <<< K[i] shr 15)                       |
|       + (K[(i+57) mod 64] <<< K[i] shr  5)                       |
|       + (K[(i+62) mod 64] <<< K[i] shr 25);                      |
*------------------------------------------------------------------*

The fixed indexes are simply selected in a way that all changes are
spread very fast through the whole array. They are 62 = (64 - 2),
3, 57 = (64 - 7), 13, 41 = (64 - 23), and 32.

The constant used in the avalance addition is equal to (e - 2.0) *
0xffffffff. We use it to fight against evil zero values. If the
whole array becomes zero during the computation, it will not remain
in that state.


2. Encryption process
=====================
>From the key schedule, the cipher gets 64 32 bit values. The first
eight ones are used for whitening the input and output values.

In the following chapter, I is the input vector, O is the output
vector, and A, B, C and D are the four 32 values of a single block.

The input whitening looks like this:

*------------------------------------------------------------------*
| A := I[0] xor K[56];                                             |
| B := I[1] xor K[57];                                             |
| C := I[2] + K[58];                                               |
| D := I[3] + K[59];                                               |
*------------------------------------------------------------------*

Then the following feistel loop is performed 14 times (with i in the
range [0..13]):

*------------------------------------------------------------------*
| T := K[i*4+0];                                                   |
| A := (A + C) <<< (B or 2);                                       |
| A := swap (A xor g0 (B, D, T));                                  |
| T := K[i*4+1];                                                   |
| C := swap (C) xor g2 (D, B, T);                                  |
| C := (C >>> (D or 2)) + A;                                       |
|                                                                  |
| T := K[i*4+2];                                                   |
| B := (B + g1 (A, C, T)) <<< (A or 2);                            |
| B := swap (B xor D);                                             |
| T := K[i*4+3];                                                   |
| D := swap (D) xor B;                                             |
| D := (D >>> (C or 2)) + g3(C, A, T);                             |
*------------------------------------------------------------------*

The swap function is a simple bit transformation which exchanges the
bytes of the value:

swap (a) := ((a and 0x000000ff) << 24)
        or ((a and 0x0000ff00) << 8)
        or ((a and 0x00ff0000) >> 8)
        or ((a and 0xff000000) >> 24)

Together with the rotations <<< and >>>, this mixes the bits
well arround.

g0, g1, g2 and g3 are functions which mix the bits of the 3
arguments.

g0(a,b,c) := f0(a,b,c) + ((f1 (a,b,c) <<< 8)
        xor ((f2 (a,b,c) <<< 16) + (f3(a,b,c) <<< 24)))
g1(a,b,c) := f1(a,b,c) xor ((f2 (a,b,c) <<< 8)
        + ((f3 (a,b,c) <<< 16) xor (f0(a,b,c) <<< 24)))
g2(a,b,c) := f2(a,b,c) + ((f3 (a,b,c) <<< 8)
        xor ((f0 (a,b,c) <<< 16) + (f1(a,b,c) <<< 24)))
g3(a,b,c) := f3(a,b,c) xor ((f0 (a,b,c) <<< 8)
        + ((f1 (a,b,c) <<< 16) xor (f2(a,b,c) <<< 24)))

They are formed using these four primitive functions f0 to f3:

f0(a,b,c) := c xor (a and (b xor c))
f1(a,b,c) := (a and c) or (c and (a or b))
f2(a,b,c) := (a or not b) xor c
f3(a,b,c) := a xor b xor c

The tables for studying them in detail are:

*------------------------------------------------------------------*
|                                                                  |
|      c xor (a and (b xor c))         (a or not b) xor c          |
|                                                                  |
|         a |b\c| 0 | 1 |                a |b\c| 0 | 1 |           |
|        ---+---+---+---+               ---+---+---+---+           |
|           | 0 |   | X |                  | 0 | X |   |           |
|         0 +---+---+---+                0 +---+---+---+           |
|           | 1 |   | X |                  | 1 |   | X |           |
|        ---+---+---+---+               ---+---+---+---+           |
|           | 0 |   |   |                  | 0 | X |   |           |
|         1 +---+---+---+                1 +---+---+---+           |
|           | 1 | X | X |                  | 1 | X |   |           |
|        ---+---+---+---+               ---+---+---+---+           |
|                                                                  |
|   (a and b) or (c and (a or b))         a xor b xor c            |
|                                                                  |
|         a |b\c| 0 | 1 |                a |b\c| 0 | 1 |           |
|        ---+---+---+---+               ---+---+---+---+           |
|           | 0 |   |   |                  | 0 |   | X |           |
|         0 +---+---+---+                0 +---+---+---+           |
|           | 1 |   | X |                  | 1 | X |   |           |
|        ---+---+---+---+               ---+---+---+---+           |
|           | 0 |   | X |                  | 0 | X |   |           |
|         1 +---+---+---+                1 +---+---+---+           |
|           | 1 | X | X |                  | 1 |   | X |           |
|        ---+---+---+---+               ---+---+---+---+           |
|                                                                  |
*------------------------------------------------------------------*

The 4 functions have the property that zero and one values have the
same chance to appear for each function, the output of them is not
equal for two different tripples of input values, and there is no
triple of input values for which all functions are all zero or all
one.

Using addition and xor mixed should again increase the
non-linearity of the whole function.

The rotations should never be zero, so I set the second bit of
the shift argument. This way it is impossible to avoid them; i.e
their second operator cannot become zero. On the other hand, only
4 of the possible 5 bits are used.

Finally, an output whitening is added.

*------------------------------------------------------------------*
| O[0] := A + K[60];                                               |
| O[1] := B + K[61];                                               |
| O[2] := C xor K[62];                                             |
| O[3] := D xor K[63];                                             |
*------------------------------------------------------------------*


3. Cryptoanalysis
=================
This is a general, informative discussion how well different
attacks against this cipher might work. Of course real attacks
might change everything stated here. I just wanted to show that
I did my best.

[I] Statistical attacks
=======================
The most primitive condition is that the plaintext and ciphertext
should be statistically independent. If that isn't the case, even
ciphertext attacks are dangerous.  Second, each output bit of the
ciphertext should depend upon each input bit of the plaintext.

Whirl surely satisfy these criterions. Plaintext attacks are
therefore not of practical interest.

[II] Weak keys
==============
There should be no weak keys. Or at least, weak keys should be
easily detectable.

There is one 2048 bit key which generates the zero schedule, but
due of the one-way hash nature of the key schedule it is extremely
hard to compute. And even with the zero schedule the output would
still look like random data thanks to the Feistel functions g0 to
g3. So I assume there are no weak keys.

[III] Differential attack
=========================
To resist differential cryptanalysis, it should depend upon the
bits in the key, which ciphertext bits are influenced by which
plaintext bits.

The key schedule has indeed very big influence on the bits which
are actually changed; only those parts used for the whitening
have no influence.

[IV] Linear Cryptoanalysis
==========================
To resist against linear cryptanalysis, an algorithm has to be
non-linear, i.e. none of the ciphertext bits should depend in a
linear way on plaintext and key bits, so one can't form an
formular system to guess bits of the key schedule from the values
of plaintext and ciphertext bits.

There are two ways by which this cipher tries to create
nonlinearity. First we have a Feistel network which combines xor
and addition - and is therefore nonlinear. Second we have an
algebraic line of operations - rotation, addition, byte swap and
xor - which mix the data and the keys, which should also create
much nonlinearity because these operations are incompatible. I
believe that the algorithm is highly nonlinear and can resist
linear attacks very well.

[V] Weaknesses
==============
I would be very surprised if the key schedule would be a problem.

The main flow of the algorithm uses two stronger algorithmic
operations, and two weaker. The strong ones are addition and
rotation, the two weaker are xor and byte swap. The problem with
xor and swap is, if you apply them two times, the result is the
original value. Therefore one should at least wait as long as
possible before applying them the next time. This minimizes
these problems. But I think there could be still attacks using
this fact.

The asymmetry of the algorithm might be a problem. The loop over
B and D looks worse than that over A and C to me. While A and C
influence each other with some indirect Pseudo Hadamard
Transformation, B and D only use a variant with xor instead of
addition. And the functions g0 and g2 have two additions while
g1 and g3 only have one.

I'm still wondering if I selected the f0 to f3 functions well
enough. I basically wanted some kind of S-Boxes without wasting
masses of memory.

[VI] Conclusion
===============
Therefore the basic preconditions for a hard to break cipher are
all met. At least I think so.

Additionally, if your computer implements dynamic rotation and
integer addition in constant time, the algorithm can't be timing
attacked.


4. Models
=========

The avalanche addition has been seen in SHA-1. The main
inspiration for using this was however an dispute between me
and Tom St Denis on news:sci.crypt, where Tom called another
loop in a hash function designed by him an 'avalanche addition',
which it wasn't.

SHA-1 and RIPE MD 160 also used some of the bitwise operations
which are used to form the g0 to g3 functions of the Feistel
Network. Blowfish concatenates the outputs of its S-Boxes the
same way as the g functions used here.

A Feistel network, or something like it, is used in even more
modern ciphers. RC5 is a good example for a cipher which looks
like a Feistel, but isn't. IDEA is another one.

The constant used in the avalanche addition is defined nearly
the same way (and with exactly the same value) in RC5.

The overall design looks a little bit like a mixture of RC5
and Twofish.


Appendix A:
Test vectors

1. K: key data
2. Key schedule
3. Round data, numbered in hexadecimal
4. I: input plaintext
   O: output ciphertext
   C: check decrypt

The test vectors can be created with the C program of appendix B.


K: 00000000000000000000000000000000
===================================
d62c090d 07dd30d8 a0267a31 ae271315
14285415 912e7119 b7f09ea7 5d8fc825
1dcce831 6b3b56cb b22391b1 3289ecfc
ed4d8473 fd05e455 b64466ac b00e87b7
df01ec9d 9c738f5a f57989af 58344bfe
ab6009c0 7d8b8ec9 36421770 03af9567
42325247 60f8e3e1 c8e886de 00563aec
899a395e 07a4b540 30b40fd4 e76796a6
a50979de b50788c5 83cac2f1 20e831bf
5f7a0126 6d4cc8c3 b75f3252 808e3352
eccc3f91 95a6995c 440cf5b7 a8d38bf3
5bb17529 a9144ca1 b6194300 808ea8a9
b3d7cc16 223b4207 c9fc7b7a bad3883e
3f0e41a4 cc7201ed 8d659d9c 1323e905
52928e89 2ec63abd 35cba498 bfd9c0f7
6fb7deb0 bdd0b25e 47366527 4ea9deac
===================================
0: 52928e892ec63abd35cba498bfd9c0f7
1: 70fbc8b4a37fe2f291871169c7289670
2: 63df437ecd4eaa2157b6efa35bc80857
3: fccae531378367b517aa6c4f12b191e3
4: 1c5eea2a33259cb73bb28f7992678e04
5: 89553ecdc5914436477b00b956e997f5
6: 4999ac99ff044758ac42668fb821032c
7: 03d4c2dac08490b49c70beb445257705
8: 91e5133a0cbc1af83f57a45a8fe86b6a
9: 77a599bb1ff3d5e97cb40e854950856c
a: 55257dd0fde8abee438704c8f943c53c
b: 815928d9a9c927d10ce51953528426fd
c: 2c230b75686f3c0863a377c5667d9ed4
d: a51d783a671499fe971233a55f362441
e: c7088add5cff452815ccab1876ef02a4
===================================
I: 00000000000000000000000000000000
O: 5750ac2d9f2e92ca52face3f3846dc08
C: 00000000000000000000000000000000


K: 80000000000000000000000000000000
===================================
eb11595b 4d6d1842 7c88824f e8f8d172
5faea335 b84f3f1d a3944648 d45fa0a0
7650e856 1d91e274 337c5e0a 079cc929
9c287f6d 7c59ca32 20ce67ec 21002a0e
a8d056fe 065d51e7 fa0b6955 3b8e777f
f31edb74 87908cfe 7ca04ae2 d85aed66
e4d83cc8 deeb37cf 702ca7f7 9f5458f3
30b6f254 c07fb2b1 b4c225a2 efc31b99
a064ec71 2c9c923d 9b0fb9cb 398d4bad
d5eda686 8b7928a2 bdddb866 6541ddc1
5f2eff0e 93bdc79d ff6d333f aa4c7576
6d1a100f 3b3f991f ba18e7bc 10168051
6ea46a2f 9a542ee9 1aeb88f0 212dbca2
d267623d 54ed68f6 21f9843a 71768e8f
ffe84e4c ce49f413 18c7bd56 35638c40
59ab847d 3ea90727 974f256a 8dad8911
===================================
0: ffe84e4cce49f41318c7bd5635638c40
1: ce05ed8186c740d225e3ec67b52ec356
2: a27f4b7b9bc03da2b9ff63e688c7271d
3: 5961f8dd360a913c26884215edb19a3c
4: 7f811814a97af416051609a5a98916e0
5: 7a8106608c363856c521a2cff5c6c197
6: 42ea94c8c336266927955d40313cca73
7: 53938314527f7dba255d43b81d576d19
8: 8ca3d1550dd471f834c096902dbbd697
9: f3499be90c4d59bdd2535d6bd1112791
a: 08b38b540168bdb50e90d7c13d8e4e4c
b: bd147bf97745ef2df744ed828076f5d8
c: a2f1b4404edd3575be81ba4eda0fadf3
d: 3777a1ea4a401b279ddc99dd2a5c6811
e: 1ac65342906b8e0f012c7fdcbc80882c
===================================
I: 00000000000000000000000000000000
O: c11acec551c286e896635ab6312d013d
C: 00000000000000000000000000000000


K: 61616161616161616161616161616161
===================================
c522217c c9300f59 3534e876 f1b4af0d
f393ff2d 56c568f1 6e912a45 5675fcad
41ef1983 b6e95fe4 35d97c60 7488cdf7
6126b418 4e12d96a 2fe4d45d 065343b5
21db5142 f64fcc68 1c9189a5 e2fead30
c8d0d65a 702f718f 81ca7660 3c504879
5ad2a59e 86836431 ab7451ba ab8c0817
5f199f2e 8b84dc63 70c208a9 5365ddc3
62a14bce 30698747 d4262d2d 0f66b93d
da9c95d4 97b2f2e1 d9f9e068 b06b6c4a
e84a2191 344d546b 19389963 b593b058
456771f5 7f3fe46f 6ecd8b9a 27675c7b
a192919d df6a9afa 4d5e6d46 e6749c62
71470bd5 72f9ac0c 5eb305d4 4bbb0e79
157991b5 b5cb32eb e3c9c2cf 8c2b7774
6096c862 4beddedd 55894b8f 759a42ac
===================================
0: 7418f0d4d4aa538a452b2430ed8cd8d5
1: dfa70f0ebbc11d4cafa4450b63e2f107
2: 6587d2fd6613f1b6f35343f1b7d8e7c6
3: e88d4cfbb1e1a787905eeefa1ea5b585
4: 8353b4d45310127ddbb9ced550baa07f
5: 3b924bead07901f74eb2b0c3b1bf37c0
6: 6f75d7a00aa1eed77c63f1835150b843
7: 91dbef44df556b3ba1f652e178cb2f40
8: 0d7350d240cc821a83306843f73be322
9: 5fd88d9aa047a495497bad3570fc7ce6
a: a1d577a70e9b4b36c7461062d18bc9b8
b: a35db156c8b4f8931c6a9dd5e0b63aa6
c: b110be493d2608300ee1f7527f1b3452
d: 5792c8d37649e82d9a936f3801e07069
e: c065ba299663c78e4c9933b83bd639ea
===================================
I: 61616161616161616161616161616161
O: 5fcef1c74a75e8b1191078374e4c7b46
C: 61616161616161616161616161616161


K: 000102030405060708090a0b0c0d0e0f
===================================
dda1f7f0 bf443d0d 515eebaa c93b0dde
af483967 9bafa45e 5bbe8ec5 e6b7dbf3
707eb169 63673793 fe8662ab ea2b8773
b0327867 23013cba cc5f4729 0d4fd580
ccb828cd 7eba8cb9 ec6d2a2b 2b9e4f91
9ed0d5d0 21a84990 3c22c1bf d50885ed
ec084db5 34157ea1 19524032 de684301
ab981b0d c6438e41 8c79da09 d551efe8
1950b160 cebdefa3 675fb36d fab56191
86229acd e7083b64 022e35c8 4a13154f
38109248 c2c776a1 254fb6ee 5c857b6d
ab37b786 d6acd423 7359a3df 3ca4da4a
1ba847c1 af2a98cb 108f8283 923adeb0
a23ad939 1858673f 9eb08847 a932b07e
f56631a4 fdec8267 66381c55 4e6d46db
ffb8d6b2 52b2bbce a01c86ee 37b2a763
===================================
0: fa781c98b6b6eb1fedcec209124028cb
1: e1898f1ca241e2463fc43ffe95e825b1
2: 53bf58face554c03f1c1921793d4c32a
3: 53fdeb27eb72077b6b1b520f61f27049
4: a012ed450dc4a19b3dd52e1b2082d55a
5: d022c2d7304131ef13b0c5b30abda8ff
6: 2d5d418535604ed97bc3c2abddd8dc01
7: e66202e140e694aab71a2350c4cfd1c0
8: a4e24a807262e0e694a4588a731251c0
9: e07bb31174caa8d7226cb2a4f3b81c70
a: c7dcb5365d86258adbc45e2c4ec38eb5
b: 3123f34e544ab55f3a06092c512898f9
c: 613efb0b9ceeedc9af10f9f4c6651d16
d: 95e2378a48c1e1392ef3b65b4650feb2
e: a69985f2dda40d9df804ade74af22426
===================================
I: 0f1e2d3c4b5a69788796a5b4c3d2e1f0
O: a6e0af408af151cf58182b097d408345
C: 0f1e2d3c4b5a69788796a5b4c3d2e1f0



Appendix B:
A portable implementation in C.

Copyright (c) 2000 Runu Knips, Siegen, Germany.
All rights reserved. No warranty.
Email: [EMAIL PROTECTED]

There are 3 variants to compile this:
# Object file to be used in own projects
cc -O -DUSE_LONGNAME -DUSE_MULTIBLOCK -c whirl128.c

# Create the test vectors
cc -O -DTEST_VECTORS -o whirl_test whirl128.c

# Create profiling version
cc -O -DTEST_PROFILE -o whirl_test whirl128.c

*/

/*---------------------------------------------------------------*/

#include <stddef.h>  /* need size_t */

typedef unsigned uint32;    /* an unsigned able to hold 32 bit */
typedef unsigned char byte;

#define rotl(x,n)   (((x) << ((n)&31)) | ((x) >> (32-((n)&31))))
#define rotr(x,n)   (((x) >> ((n)&31)) | ((x) << (32-((n)&31))))
#define swap(x)     (0 \
        | (((x) & 0xff000000) >> 24) \
        | (((x) & 0x00ff0000) >> 8) \
        | (((x) & 0x0000ff00) << 8) \
        | (((x) & 0x000000ff) << 24) \
)
#define load(p)     (0 \
        | (((byte*)(p))[0] << 030) \
        | (((byte*)(p))[1] << 020) \
        | (((byte*)(p))[2] << 010) \
        | (((byte*)(p))[3] << 000) \
)
#define save(p, x)  do { \
        uint32 _x = (x); \
        byte *_p = (p); \
        _p[0] = (byte) (_x >> 030); \
        _p[1] = (byte) (_x >> 020); \
        _p[2] = (byte) (_x >> 010); \
        _p[3] = (byte) (_x >> 000); \
} while (0)

/*---------------------------------------------------------------*/

#ifdef USE_LONGNAME
#define setkey whirl128_setkey
#define encrypt whirl128_encrypt_block
#define decrypt whirl128_decrypt_block
#endif

#if defined(TEST_VECTORS) || defined(TEST_PROFILE)
#define TEST_MAIN

#include <stdio.h>
#include <time.h>
#define countof(v) (sizeof(v) / sizeof((v)[0]))
#endif

#ifdef TEST_VECTORS
static void putround (unsigned i,
        uint32 a, uint32 b, uint32 c, uint32 d);
#else
#define putround(i,a,b,c,d) (void)0
#endif

/*---------------------------------------------------------------*/

#define ROUNDS 14
#define KEYCNT 64  /* = 8 + 4 * ROUNDS */

struct keysched_struct {
        uint32 t[KEYCNT];
};
typedef struct keysched_struct keysched;

void setkey (keysched *k, const byte buf[], size_t len)
{
        uint32 u;
        size_t i, j;

        for (i = j = 0; j < len && i != KEYCNT; ++i) {
                u = buf[j] << 030; ++j;
                if (j < len) { u |= buf[j] << 020; ++j; }
                if (j < len) { u |= buf[j] << 010; ++j; }
                if (j < len) { u |= buf[j] << 000; ++j; }
                k->t[i] = u;
        }
        for (; i != KEYCNT; ++i)
                k->t[i] = 0;
        for (j = 0; j != 4; ++j) {
                for (i = 0; i != KEYCNT; ++i) {
                        k->t[i] = k->t[i] + 0xb7e15161
                                + rotl (k->t[(i +  3) % KEYCNT], k->t[i] >> 10)
                                + rotl (k->t[(i + 13) % KEYCNT], k->t[i] >> 25)
                                + rotl (k->t[(i + 32) % KEYCNT], k->t[i] >>  0)
                                + rotl (k->t[(i + 41) % KEYCNT], k->t[i] >> 15)
                                + rotl (k->t[(i + 57) % KEYCNT], k->t[i] >>  5)
                                + rotl (k->t[(i + 62) % KEYCNT], k->t[i] >> 20);
                }
        }
}

#define f0(a,b,c)  ((c) ^ ((a) & ((b) ^ (c))))
#define f1(a,b,c)  (((a) & (c)) | ((c) & ((a) | (b))))
#define f2(a,b,c)  (((a) | (~(b))) ^ (c))
#define f3(a,b,c)  ((a) ^ (b) ^ (c))

#define f(a,b,c,f0,f1,f2,f3) ( \
        f0 (a,b,c)                 \
+       (rotl (f1 (a,b,c), 8)      \
^       (rotl (f2 (a,b,c), 16)     \
+       rotl (f3 (a,b,c), 24)))    \
)
#define g(a,b,c,f0,f1,f2,f3) ( \
        f0 (a,b,c)                 \
^       (rotl (f1 (a,b,c), 8)      \
+       (rotl (f2 (a,b,c), 16)     \
^       rotl (f3 (a,b,c), 24)))    \
)

#define g0(a,b,c)  f(a,b,c,f0,f1,f2,f3)
#define g1(a,b,c)  g(a,b,c,f1,f2,f3,f0)
#define g2(a,b,c)  f(a,b,c,f2,f3,f0,f1)
#define g3(a,b,c)  g(a,b,c,f3,f0,f1,f2)

#define T(k,i)  ((k)->t[(i)])
#define W(k,i)  T((k), (i)+56)

void encrypt (const keysched *k, const byte *ibuf, byte *obuf)
{
        unsigned i;
        uint32 a, b, c, d;
        uint32 t, x;

        a = load (ibuf+0x0) ^ W(k, 0); b = load (ibuf+0x4) ^ W(k, 1);
        c = load (ibuf+0x8) + W(k, 2); d = load (ibuf+0xc) + W(k, 3);

        i = 0;
        do {
                putround (i, a, b, c, d);

                t = T(k, i*4+0); x = g0 (b, d, t);
                a += c; a = rotl (a, b | 2); a ^= x; a = swap (a);
                t = T(k, i*4+1); x = g2 (d, b, t);
                c = swap (c); c ^= x; c = rotr (c, d | 2); c += a;

                t = T(k, i*4+2); x = g1 (a, c, t);
                b += x; b = rotl (b, a | 2); b ^= d; b = swap (b);
                t = T(k, i*4+3); x = g3 (c, a, t);
                d = swap (d); d ^= b; d = rotr (d, c | 2); d += x;

                ++i;
        } while (i != ROUNDS);
        putround (i, a, b, c, d);

        save (obuf+0x0, a - W(k, 4)); save (obuf+0x4, b - W(k, 5));
        save (obuf+0x8, c ^ W(k, 6)); save (obuf+0xc, d ^ W(k, 7));
}

void decrypt (const keysched *k, const byte *ibuf, byte *obuf)
{
        unsigned i;
        uint32 a, b, c, d;
        uint32 t, x;

        a = load (ibuf+0x0) + W(k, 4); b = load (ibuf+0x4) + W(k, 5);
        c = load (ibuf+0x8) ^ W(k, 6); d = load (ibuf+0xc) ^ W(k, 7);

        i = ROUNDS;
        do {
                --i;
        
                t = T(k, i*4+3); x = g3 (c, a, t);
                d -= x; d = rotl (d, c | 2); d ^= b; d = swap (d);
                t = T(k, i*4+2); x = g1 (a, c, t);
                b = swap (b); b ^= d; b = rotr (b, a | 2); b -= x;

                t = T(k, i*4+1); x = g2 (d, b, t);
                c -= a; c = rotl (c, d | 2); c ^= x; c = swap (c);
                t = T(k, i*4+0); x = g0 (b, d, t);
                a = swap (a); a ^= x; a = rotr (a, b | 2); a -= c;
        } while (i != 0);

        save (obuf+0x0, a ^ W(k, 0)); save (obuf+0x4, b ^ W(k, 1));
        save (obuf+0x8, c - W(k, 2)); save (obuf+0xc, d - W(k, 3));
}

/*---------------------------------------------------------------*/

#ifdef USE_MULTIBLOCK
/*
** IMHO completely superflous - who needs EBC mode ?!? But added
** according to the requirements of the cipher contest.
*/

void whirl128_encrypt (const keysched* k, const void *input,
        void *output, size_t len)
{
        size_t x;

        for (x = 0; x < len; x += 16) {
                encrypt (k, (const char*)input + x, (char*)output + x);
        }
}

void whirl128_decrypt (const keysched* k, const void *input,
        void *output, size_t len)
{
        size_t x;

        for (x = 0; x < len; x += 16) {
                decrypt (k, (const char*)input + x, (char*)output + x);
        }
}
#endif

/*---------------------------------------------------------------*/

#ifdef TEST_MAIN

#ifdef TEST_VECTORS
static void putbuf (const char *s, const char *v)
{
        int i;
        unsigned char c;
        static const char hex[16] = {'0','1','2','3','4',
                '5','6','7','8','9','a','b','c','d','e','f'};

        for (i = 0; s[i] != 0; ++i)
                putc (s[i], stdout);
        for (i = 0; i != 16; ++i) {
                c = (unsigned char) v[i];
                putc (hex[(c >> 4) & 0xf], stdout);
                putc (hex[(c >> 0) & 0xf], stdout);
        }
        putc ('\n', stdout);
}

static void putround (unsigned i,
        uint32 a, uint32 b, uint32 c, uint32 d)
{
        char title[10], line[16];

        sprintf (title, "%x: ", i);
        save (line+0x0, a);
        save (line+0x4, b);
        save (line+0x8, c);
        save (line+0xc, d);
        putbuf (title, line);
}

static void putsched (const keysched *K)
{
        int i;

        for (i = 0; i != countof(K->t); ++i) {
                if ((i & 3) != 0) putc (' ',stdout);
                printf ("%08x", K->t[i]);
                if (((i+1) & 3) == 0) putc ('\n', stdout);
        }
}
#endif

struct test {
        char key[16];
        char ibf[16];
};

static const struct test testv[] = {
        { {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
                {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} },
        { {0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
                {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} },
        { {'a','a','a','a','a','a','a','a',
                'a','a','a','a','a','a','a','a'},
                {'a','a','a','a','a','a','a','a',
                        'a','a','a','a','a','a','a','a'} },
        { {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15},
                {0x0f,0x1e,0x2d,0x3c,0x4b,0x5a,0x69,0x78,
                        0x87,0x96,0xa5,0xb4,0xc3,0xd2,0xe1,0xf0} },
};

int main ()
{
        int i;
        char buf[16];
        keysched K;
        
#ifdef TEST_PROFILE
        clock_t t;

        /*-----------------------------------------------------*/
        printf ("100,000 times key schedule: "); fflush (stdout);
        t = clock ();
        for (i = 0; i != 100000; ++i) {
                setkey (&K, testv[0].key, 16);
        }
        t = clock () - t;
        printf ("%0.14g sec\n", (double)t / CLOCKS_PER_SEC);
        
        /*-----------------------------------------------------*/
        printf ("1,000,000 times encrypt: "); fflush (stdout);
        t = clock ();
        for (i = 0; i != 1000000; ++i) {
                encrypt (&K, testv[0].ibf, buf);
        }
        t = clock () - t;
        printf ("%0.14g sec\n", (double)t / CLOCKS_PER_SEC);
#else
        /*-----------------------------------------------------*/
        for (i = 0; i != countof(testv); ++i) {
                putc ('\n', stdout);
                setkey (&K, testv[i].key, 16);
                putbuf ("K: ", testv[i].key);
                printf ("-----------------------------------\n");
                putsched (&K);
                printf ("-----------------------------------\n");
                encrypt (&K, testv[i].ibf, buf);
                printf ("-----------------------------------\n");
                putbuf ("I: ", testv[i].ibf);
                putbuf ("O: ", buf);
                decrypt (&K, buf, buf);
                putbuf ("C: ", buf);
                putc ('\n', stdout);
        }
#endif

        return 0;
}

#endif

/*---------------------------------------------------------------*/

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


** 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