1/* LibTomCrypt, modular cryptographic library -- Tom St Denis
2 *
3 * LibTomCrypt is a library that provides various cryptographic
4 * algorithms in a highly modular and flexible manner.
5 *
6 * The library is free for all purposes without any express
7 * guarantee it works.
8 *
9 * Tom St Denis, tomstdenis@gmail.com, http://libtomcrypt.com
10 */
11#include "tomcrypt.h"
12
13/**
14 @file sober128.c
15 Implementation of SOBER-128 by Tom St Denis.
16 Based on s128fast.c reference code supplied by Greg Rose of QUALCOMM.
17*/
18
19#ifdef SOBER128
20
21#include "sober128tab.c"
22
23const struct ltc_prng_descriptor sober128_desc =
24{
25   "sober128", 64,
26    &sober128_start,
27    &sober128_add_entropy,
28    &sober128_ready,
29    &sober128_read,
30    &sober128_done,
31    &sober128_export,
32    &sober128_import,
33    &sober128_test
34};
35
36/* don't change these... */
37#define N                        17
38#define FOLD                      N /* how many iterations of folding to do */
39#define INITKONST        0x6996c53a /* value of KONST to use during key loading */
40#define KEYP                     15 /* where to insert key words */
41#define FOLDP                     4 /* where to insert non-linear feedback */
42
43#define B(x,i) ((unsigned char)(((x) >> (8*i)) & 0xFF))
44
45static ulong32 BYTE2WORD(unsigned char *b)
46{
47   ulong32 t;
48   LOAD32L(t, b);
49   return t;
50}
51
52#define WORD2BYTE(w, b) STORE32L(b, w)
53
54static void XORWORD(ulong32 w, unsigned char *b)
55{
56   ulong32 t;
57   LOAD32L(t, b);
58   t ^= w;
59   STORE32L(t, b);
60}
61
62/* give correct offset for the current position of the register,
63 * where logically R[0] is at position "zero".
64 */
65#define OFF(zero, i) (((zero)+(i)) % N)
66
67/* step the LFSR */
68/* After stepping, "zero" moves right one place */
69#define STEP(R,z) \
70    R[OFF(z,0)] = R[OFF(z,15)] ^ R[OFF(z,4)] ^ (R[OFF(z,0)] << 8) ^ Multab[(R[OFF(z,0)] >> 24) & 0xFF];
71
72static void cycle(ulong32 *R)
73{
74    ulong32 t;
75    int     i;
76
77    STEP(R,0);
78    t = R[0];
79    for (i = 1; i < N; ++i) {
80        R[i-1] = R[i];
81    }
82    R[N-1] = t;
83}
84
85/* Return a non-linear function of some parts of the register.
86 */
87#define NLFUNC(c,z) \
88{ \
89    t = c->R[OFF(z,0)] + c->R[OFF(z,16)]; \
90    t ^= Sbox[(t >> 24) & 0xFF]; \
91    t = RORc(t, 8); \
92    t = ((t + c->R[OFF(z,1)]) ^ c->konst) + c->R[OFF(z,6)]; \
93    t ^= Sbox[(t >> 24) & 0xFF]; \
94    t = t + c->R[OFF(z,13)]; \
95}
96
97static ulong32 nltap(struct sober128_prng *c)
98{
99    ulong32 t;
100    NLFUNC(c, 0);
101    return t;
102}
103
104/**
105  Start the PRNG
106  @param prng     [out] The PRNG state to initialize
107  @return CRYPT_OK if successful
108*/
109int sober128_start(prng_state *prng)
110{
111    int                   i;
112    struct sober128_prng *c;
113
114    LTC_ARGCHK(prng != NULL);
115
116    c = &(prng->sober128);
117
118    /* Register initialised to Fibonacci numbers */
119    c->R[0] = 1;
120    c->R[1] = 1;
121    for (i = 2; i < N; ++i) {
122       c->R[i] = c->R[i-1] + c->R[i-2];
123    }
124    c->konst = INITKONST;
125
126    /* next add_entropy will be the key */
127    c->flag  = 1;
128    c->set   = 0;
129
130    return CRYPT_OK;
131}
132
133/* Save the current register state
134 */
135static void s128_savestate(struct sober128_prng *c)
136{
137    int i;
138    for (i = 0; i < N; ++i) {
139        c->initR[i] = c->R[i];
140    }
141}
142
143/* initialise to previously saved register state
144 */
145static void s128_reloadstate(struct sober128_prng *c)
146{
147    int i;
148
149    for (i = 0; i < N; ++i) {
150        c->R[i] = c->initR[i];
151    }
152}
153
154/* Initialise "konst"
155 */
156static void s128_genkonst(struct sober128_prng *c)
157{
158    ulong32 newkonst;
159
160    do {
161       cycle(c->R);
162       newkonst = nltap(c);
163    } while ((newkonst & 0xFF000000) == 0);
164    c->konst = newkonst;
165}
166
167/* Load key material into the register
168 */
169#define ADDKEY(k) \
170   c->R[KEYP] += (k);
171
172#define XORNL(nl) \
173   c->R[FOLDP] ^= (nl);
174
175/* nonlinear diffusion of register for key */
176#define DROUND(z) STEP(c->R,z); NLFUNC(c,(z+1)); c->R[OFF((z+1),FOLDP)] ^= t;
177static void s128_diffuse(struct sober128_prng *c)
178{
179    ulong32 t;
180    /* relies on FOLD == N == 17! */
181    DROUND(0);
182    DROUND(1);
183    DROUND(2);
184    DROUND(3);
185    DROUND(4);
186    DROUND(5);
187    DROUND(6);
188    DROUND(7);
189    DROUND(8);
190    DROUND(9);
191    DROUND(10);
192    DROUND(11);
193    DROUND(12);
194    DROUND(13);
195    DROUND(14);
196    DROUND(15);
197    DROUND(16);
198}
199
200/**
201  Add entropy to the PRNG state
202  @param in       The data to add
203  @param inlen    Length of the data to add
204  @param prng     PRNG state to update
205  @return CRYPT_OK if successful
206*/
207int sober128_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng)
208{
209    struct sober128_prng *c;
210    ulong32               i, k;
211
212    LTC_ARGCHK(in != NULL);
213    LTC_ARGCHK(prng != NULL);
214    c = &(prng->sober128);
215
216    if (c->flag == 1) {
217       /* this is the first call to the add_entropy so this input is the key */
218       /* inlen must be multiple of 4 bytes */
219       if ((inlen & 3) != 0) {
220          return CRYPT_INVALID_KEYSIZE;
221       }
222
223       for (i = 0; i < inlen; i += 4) {
224           k = BYTE2WORD((unsigned char *)&in[i]);
225          ADDKEY(k);
226          cycle(c->R);
227          XORNL(nltap(c));
228       }
229
230       /* also fold in the length of the key */
231       ADDKEY(inlen);
232
233       /* now diffuse */
234       s128_diffuse(c);
235
236       s128_genkonst(c);
237       s128_savestate(c);
238       c->nbuf = 0;
239       c->flag = 0;
240       c->set  = 1;
241    } else {
242       /* ok we are adding an IV then... */
243       s128_reloadstate(c);
244
245       /* inlen must be multiple of 4 bytes */
246       if ((inlen & 3) != 0) {
247          return CRYPT_INVALID_KEYSIZE;
248       }
249
250       for (i = 0; i < inlen; i += 4) {
251           k = BYTE2WORD((unsigned char *)&in[i]);
252          ADDKEY(k);
253          cycle(c->R);
254          XORNL(nltap(c));
255       }
256
257       /* also fold in the length of the key */
258       ADDKEY(inlen);
259
260       /* now diffuse */
261       s128_diffuse(c);
262       c->nbuf = 0;
263    }
264
265    return CRYPT_OK;
266}
267
268/**
269  Make the PRNG ready to read from
270  @param prng   The PRNG to make active
271  @return CRYPT_OK if successful
272*/
273int sober128_ready(prng_state *prng)
274{
275   return prng->sober128.set == 1 ? CRYPT_OK : CRYPT_ERROR;
276}
277
278/* XOR pseudo-random bytes into buffer
279 */
280#define SROUND(z) STEP(c->R,z); NLFUNC(c,(z+1)); XORWORD(t, out+(z*4));
281
282/**
283  Read from the PRNG
284  @param out      Destination
285  @param outlen   Length of output
286  @param prng     The active PRNG to read from
287  @return Number of octets read
288*/
289unsigned long sober128_read(unsigned char *out, unsigned long outlen, prng_state *prng)
290{
291   struct sober128_prng *c;
292   ulong32               t, tlen;
293
294   LTC_ARGCHK(out  != NULL);
295   LTC_ARGCHK(prng != NULL);
296
297#ifdef LTC_VALGRIND
298   zeromem(out, outlen);
299#endif
300
301   c = &(prng->sober128);
302   t = 0;
303   tlen = outlen;
304
305   /* handle any previously buffered bytes */
306   while (c->nbuf != 0 && outlen != 0) {
307      *out++ ^= c->sbuf & 0xFF;
308       c->sbuf >>= 8;
309       c->nbuf -= 8;
310       --outlen;
311   }
312
313#ifndef LTC_SMALL_CODE
314    /* do lots at a time, if there's enough to do */
315    while (outlen >= N*4) {
316      SROUND(0);
317      SROUND(1);
318      SROUND(2);
319      SROUND(3);
320      SROUND(4);
321      SROUND(5);
322      SROUND(6);
323      SROUND(7);
324      SROUND(8);
325      SROUND(9);
326      SROUND(10);
327      SROUND(11);
328      SROUND(12);
329      SROUND(13);
330      SROUND(14);
331      SROUND(15);
332      SROUND(16);
333      out    += 4*N;
334      outlen -= 4*N;
335    }
336#endif
337
338    /* do small or odd size buffers the slow way */
339    while (4 <= outlen) {
340      cycle(c->R);
341      t = nltap(c);
342      XORWORD(t, out);
343      out    += 4;
344      outlen -= 4;
345    }
346
347    /* handle any trailing bytes */
348    if (outlen != 0) {
349      cycle(c->R);
350      c->sbuf = nltap(c);
351      c->nbuf = 32;
352      while (c->nbuf != 0 && outlen != 0) {
353          *out++ ^= c->sbuf & 0xFF;
354          c->sbuf >>= 8;
355          c->nbuf -= 8;
356          --outlen;
357      }
358    }
359
360    return tlen;
361}
362
363/**
364  Terminate the PRNG
365  @param prng   The PRNG to terminate
366  @return CRYPT_OK if successful
367*/
368int sober128_done(prng_state *prng)
369{
370   LTC_ARGCHK(prng != NULL);
371   return CRYPT_OK;
372}
373
374/**
375  Export the PRNG state
376  @param out       [out] Destination
377  @param outlen    [in/out] Max size and resulting size of the state
378  @param prng      The PRNG to export
379  @return CRYPT_OK if successful
380*/
381int sober128_export(unsigned char *out, unsigned long *outlen, prng_state *prng)
382{
383   LTC_ARGCHK(outlen != NULL);
384   LTC_ARGCHK(out    != NULL);
385   LTC_ARGCHK(prng   != NULL);
386
387   if (*outlen < 64) {
388      *outlen = 64;
389      return CRYPT_BUFFER_OVERFLOW;
390   }
391
392   if (sober128_read(out, 64, prng) != 64) {
393      return CRYPT_ERROR_READPRNG;
394   }
395   *outlen = 64;
396
397   return CRYPT_OK;
398}
399
400/**
401  Import a PRNG state
402  @param in       The PRNG state
403  @param inlen    Size of the state
404  @param prng     The PRNG to import
405  @return CRYPT_OK if successful
406*/
407int sober128_import(const unsigned char *in, unsigned long inlen, prng_state *prng)
408{
409   int err;
410   LTC_ARGCHK(in   != NULL);
411   LTC_ARGCHK(prng != NULL);
412
413   if (inlen != 64) {
414      return CRYPT_INVALID_ARG;
415   }
416
417   if ((err = sober128_start(prng)) != CRYPT_OK) {
418      return err;
419   }
420   if ((err = sober128_add_entropy(in, 64, prng)) != CRYPT_OK) {
421      return err;
422   }
423   return sober128_ready(prng);
424}
425
426/**
427  PRNG self-test
428  @return CRYPT_OK if successful, CRYPT_NOP if self-testing has been disabled
429*/
430int sober128_test(void)
431{
432#ifndef LTC_TEST
433   return CRYPT_NOP;
434#else
435   static const struct {
436     int keylen, ivlen, len;
437     unsigned char key[16], iv[4], out[20];
438   } tests[] = {
439
440{
441   16, 4, 20,
442
443   /* key */
444   { 0x74, 0x65, 0x73, 0x74, 0x20, 0x6b, 0x65, 0x79,
445     0x20, 0x31, 0x32, 0x38, 0x62, 0x69, 0x74, 0x73 },
446
447   /* IV */
448   { 0x00, 0x00, 0x00, 0x00 },
449
450   /* expected output */
451   { 0x43, 0x50, 0x0c, 0xcf, 0x89, 0x91, 0x9f, 0x1d,
452     0xaa, 0x37, 0x74, 0x95, 0xf4, 0xb4, 0x58, 0xc2,
453     0x40, 0x37, 0x8b, 0xbb }
454}
455
456};
457   prng_state    prng;
458   unsigned char dst[20];
459   int           err, x;
460
461   for (x = 0; x < (int)(sizeof(tests)/sizeof(tests[0])); x++) {
462       if ((err = sober128_start(&prng)) != CRYPT_OK) {
463          return err;
464       }
465       if ((err = sober128_add_entropy(tests[x].key, tests[x].keylen, &prng)) != CRYPT_OK) {
466          return err;
467       }
468       /* add IV */
469       if ((err = sober128_add_entropy(tests[x].iv, tests[x].ivlen, &prng)) != CRYPT_OK) {
470          return err;
471       }
472
473       /* ready up */
474       if ((err = sober128_ready(&prng)) != CRYPT_OK) {
475          return err;
476       }
477       XMEMSET(dst, 0, tests[x].len);
478       if (sober128_read(dst, tests[x].len, &prng) != (unsigned long)tests[x].len) {
479          return CRYPT_ERROR_READPRNG;
480       }
481       sober128_done(&prng);
482       if (XMEMCMP(dst, tests[x].out, tests[x].len)) {
483#if 0
484          printf("\n\nSOBER128 failed, I got:\n");
485          for (y = 0; y < tests[x].len; y++) printf("%02x ", dst[y]);
486          printf("\n");
487#endif
488          return CRYPT_FAIL_TESTVECTOR;
489       }
490   }
491   return CRYPT_OK;
492#endif
493}
494
495#endif
496
497
498/* $Source: /cvs/libtom/libtomcrypt/src/prngs/sober128.c,v $ */
499/* $Revision: 1.8 $ */
500/* $Date: 2006/11/05 00:11:36 $ */
501