AES
Analysis of the RefCode, OptCCode and AddCode submissions

NIST API (RefCode and OptCCode)
Cast 256 Crypton DEAL DFC E2 Frog HPC Mars RC6 Rijndael Safer + Serpent Twofish Loki 97 Magenta
API Headers OK OK OK OK OK OK OK OK OK OK Incompatible change (o) OK OK OK OK
makeKey OK no ASCII conversion (d) have to malloc (f) OK OK OK have to free (k) bad return value (m) OK bad prototype (n) OK OK OK (q) OK OK
cipherInit (a) init not complete (c) no ASCII conversion (d) OK no NULL IV (h) OK OK init not complete and have to free (k) (l) bad return value (m) OK bad prototype (n) OK OK OK OK OK
blockEncrypt OK OK OK bad inputLen value (i) OK returns TRUE (j) OK OK OK OK returns TRUE (j) OK OK returns TRUE (j) OK
Compilation (b) OK Take care (e) Case of filenames (g) OK OK OK OK OK OK OK OK One letter macro (p) Take care (e) OK OK

Notes on implementation of API functions

(a)
The NIST API is not fair here... the parameter IV is a string that is translated into an array of BYTE and stored in the cipherInstance. This is not the same as the parameter keyMaterial for makeKey, that is stored unchanged in the keyInstance.
(b)
We make a library with all the API functionalities, that will be linked with our test programs.
(c)
We have to initialise cipher->verboseoutfile to NULL. We don't have this problem with OptCCode.
(d)
keyMaterial should be a string of keyLen/4 ASCII characters describing the hex value of the key. This function uses the binary encoding of keyLen/8 characters.
(e)
If you want to build some crypto library using the NIST API, you need to include the main file, which defines a main function that you may have to delete. To do some timings, this file uses the value of a constant CLOCKS_PER_SEC, that is not defined in some operating systems (e.g. SunOS 4.1.4)
(f)
As stated in deal.h, you have to allocate key->kss before calling makeKey.
(g)
The file names are not consistent : deal.c, dealref.c and dealkeys.c want to include DEAL.h when dealapi.c needs deal.h.
(h)
Segmentation fault if IV is NULL or non valid, even in ECB mode.
(i)
Here, the parameter inputLen is the size of input in bytes instead of bits (same behaviour as the Java API). Still the return value is the number of bits enciphered.
(j)
The function returns TRUE instead of the number of bits enciphered.
(k)
makeKey allocates three blocks of memory key->KS, key->hpc_kmbits and ((U64**)key->KS)[HPCM] that may never be freed, and cipherInit allocates one block of memory that cannot be freed.
(l)
We have to initialise cipher->blockSize to 128.
(m)
On success, makeKey and cipherInit return 0 instead of TRUE.
(n)
makeKey and cipherInit are defined with an additional argument : blockLen. This is not compatible with the API...
(o)
AES_SAFERPlus.h defines BAD_KEY_LEN without saying it is algorithm specific. This definition changes the values of BAD_KEY_INSTANCE, BAD_CIPHER_MODE and BAD_CIPHER_STATE.
(p)
The file serpent-api.h defines the macro r with value 32 ! This may cause conflicts with variable names or struct members. We don't have this problem with OptCCode.
(q)
Warning! the function returns BAD_KEY_MAT if it was compiled with the bad endian definition...
Decryption of an encrypted message (RefCode and OptCCode)
Cast 256 Crypton DEAL DFC E2 Frog HPC Mars RC6 Rijndael Safer + Serpent Twofish Loki 97 Magenta
ECB OK OK OK OK OK OK OK OK OK OK OK OK OK OK OK
CBC (a) OK # OK # (c) OK # OK OK OK # OK # OK OK # OK OK # OK # OK OK OK
CFB1 (b) OK # no # OK # OK OK no # (d) OK # no (e) OK # OK (f) OK # OK # (g) OK # (g) OK OK

Notes

It is OK when the decryption give the original message. I made the tests with a 128 bits message.
(a)
It is not clear in the NIST API if an encryption or decription should change cipher->IV. We note with a # the implementations that do change cipher->IV.
(b)
Note that the NIST API cannot be used to decrypt in CFB1 mode, since you need to use the DIR_ENCRYPT key schedule with the blockDecrypt function and that is forbidden (returns a BAD_KEY_MAT).
I changed the API to allow DIR_ENCRYPT and only this key schedule for blockDecrypt in CFB1 mode. I made the tests with this modified API.
DEAL and FROG implementors had already noticed this problem.
(c)
Only OptCCode decrypts well, but the NIST did not require RefCode implementations of CBC and CFB1.
(d)
Encryption : very weak ! only the first byte is changed for 16 bytes of input...
Decryption : missing a break in the C source to have a valid return value.
(e)
CFB1 mode is only implemented for 1 bit messages... and cannot be decrypted.
(f)
CFB1 mode is only implemented in OptCCode, since the NIST did not require RefCode implementations of CBC and CFB1.
(g)
Only RefCode decrypts well, but the NIST did require OptCCode implementation of CFB1.
Compilation and execution of the tests (RefCode and OptCCode)
Cast 256 Crypton DEAL DFC E2 Frog HPC Mars RC6 Rijndael Safer + Serpent Twofish Loki 97 Magenta
Little Endian OK (a) OK OK OK OK OK OK for ix86 (d) OK for ix86 (f) OK OK OK OK Not easy (m) OK OK
Big Endian OK (a) OK OK OK Fails (c) OK Fails (d) (e) SIGBUS (f) (g) Fails (i) OK OK OK Not easy (m) OK OK
64 bit long OK (b) OK OK OK Fails (c) OK Fails (d) OK (f) (h) Fails (j) OK (k) OK Not easy (l) Not easy (m) (n) Not easy (o) OK

Notes : ANSI conformance and portability of the C source

When the encryption of the same clear text with the same key does not give the same result on different machines, this is a serious problem because you cannot have an encrypted channel between these machines. Such incompatibility is often due to endianess problems, but it cannot happen if the source is strict ANSI. The reference is the ciphering done by some ix86 system.
(a)
You have to change the file cast.h : there is a macro littleendian that allow portability.
(b)
You have to change the type uns32 in cast.h.
(c)
Everything is OK with the RefCode, but for the OptCCode running the tests makes a SIGSEGV...
(d)
In hpc-ansi.c there may be a SIGBUS due to a cast from BYTE* to U64* in makeKey.
(e)
hpc-ansi.c and hpc-gcc.c give wrong results, depending of the internal representation of integers.
(f)
There is a serious SIGBUS problem due to a cast from BYTE* to WORD* in cipherInit : that does not respect alignment.
(g)
You have a test for endianess in the definition of SWAP_BYTES in sbox.c, mars-ref.c and mars-opt.c, which you may have to enhance for other architectures.
(h)
You have to change the type WORD in aes.h.
(i)
It is OK with RefCode but give wrong results with OptCCode.
(j)
If you change all instance of long to some 32-bits integer type, it works for RefCode but not for OptCCode.
(k)
You have to change word32 in rijndael-alg-fst.h.
(l)
For OptCCode, you have to change every long in all the files.
(m)
You have to change the file platform.h which detects little-endian only if _M_IX86 is defined. But if you set _M_IX86 for some non-Windows environnment, there may be other problems... So the adaptation to non-Wintel architectures is a bit sophisticated.
(n)
You have to change the definition of DWORD in aes.h and the casts from pointer to int in twofish.c (those casts check the alignment, cf. the remark (f) for Mars).
(o)
You have to change every long in the files aestime.c, loki97.c and loki97.h to an int.
ANSI C timings : OptCCode, using NIST API
Cast 256 Crypton DEAL DFC (*) E2 Frog HPC (*) Mars RC6 Rijndael Safer + Serpent Twofish Loki 97 Magenta
Cyrix 6x86MX 166 MHz Linux 2.0 57µs 9460c
1.87MB/s 1355c
4.4µs   730c
1.20MB/s 2111c
224µs 37000c
0.56MB/s 4523c
134µs 22000c
0.46MB/s 5506c
41µs 6800c
2.08MB/s 1218c
18ms 3000000c
1.49MB/s 1700c
1.5ms 250000c
0.45MB/s 5629c
36µs 6000c
2.92MB/s 867c
28µs 4650c
2.45MB/s 1034c
145µs 24000c
2.95MB/s 859c
 21µs    3500c
0.92MB/s 2753c
83µs 14000c
1.42MB/s 1784c
114µs 19000c
2.18MB/s 1162c
138µs 23000c
0.39MB/s 6495c
5.1µs   850c
0.14MB/s 18100c
Pentium II 200 MHz Linux 2.0 40µs 8050c
2.61MB/s 1168c
3.0µs   600c
2.62MB/s 1166c
152µs 30500c
0.79MB/s 3862c
60µs 12000c
0.93MB/s 3270c
18µs 3680c
4.17MB/s 732c
10.5ms 2100000c
1.25MB/s 2444c
1.3ms 260000c
0.57MB/s 5366c
24µs 4800c
3.83MB/s 796c
14µs 2800c
3.65MB/s 836c
99µs 19800c
4.60MB/s 862c
 15µs    3000c
1.31MB/s 2324c
58µs 11600c
1.67MB/s 1826c
63µs 12600c
3.81MB/s 800c
84µs 16800c
0.66MB/s 4610c
2.4µs   480c
0.10MB/s 30400c
UltraSparc 270 MHz SunOS 5.6 23µs 6200c
3.67MB/s 1123c (a)
 2.5µs    675c
4.90MB/s 841c (b)
115µs 31000c
1.51MB/s 2728c (b)
60µs 16200c
1.02MB/s 4039c (b)
275µs 74Kc
0.18MB/s 23Kc (e,b)
10ms 2700000c
1.52MB/s 2710c (a)
1.1ms 300000c
0.83MB/s 4964c
core 61µs 16500c
3.45MB/s 1194c (f,b)
40µs 10800c
4.26MB/s 990c (b)
 11µs    3000c
1.20MB/s 3433c (b)
 31µs   8400c
3.73MB/s 1105c(g,b)
52µs 14000c
4.24MB/s 972c (b)
49µs 13200c
1.19MB/s 3462c (b)
 2.7µs    730c
0.27MB/s 15260c
Alpha EV56 500 MHz Linux 2.1 18µs 9000c
5.01MB/s 1523c
1.6µs    800c
2.67MB/s 2860c (c)
131µs 65500c
2.25MB/s 3390c
48µs 24000c
1.33MB/s 5735c (d)
0.27ms 135000c
0.19MB/s 40Kc (e)
7.7ms 3800000c
1.38MB/s 5530c
0.62ms 310000c
1.45MB/s 5260c
12µs 6000c
8.63MB/s 884c
20µs 10000c
8.08MB/s 944c (e)
93µs 46500c
5.81MB/s 1313c
 14µs    7000c
0.59MB/s 12930c
42µs 21000c
4.93MB/s 1548c
42µs 21000c
8.22MB/s 928c
40µs 20000c
1.37MB/s 5570c
2.4µs 1200c
0.30MB/s 25430c
The timings are measured for 128 bits keys and 128 bits messages. There is the time for key setup in ms or µs and the rate in Mbytes/sec for ECB mode. They measure the performance of the algorithm for a multi-architecture and portable communication software.
All compilations done with gcc -O9 version 2.8.1, unless we found a faster compiler (e.g. the vendor's compiler).
(*)
I don't accept 64-bits integer types (long long, or long for Alpha) because it is not ANSI. It should be allowed by C9X.
(a)
I use gcc -O9 version egcs-2.91.45 : encryption is much faster.
(b)
I use acc -fast version SWC-4.2 (Sun C compiler) : encryption is much faster.
(c)
I have changed the files crypton.h and cryptonsbox.c to set the type DWORD to unsigned int : the encryption speed gains 12%.
(d)
I have changed the files dfc.h to set the types u32 and s32 to (un)signed int : the encryption speed gains 6%.
(e)
This timings are done with RefCode, since OptCCode fails.
(f)
I use RefCode : encryption is faster.
(g)
Take care : these timings correspond to an encryption with bad endianess.

Back