progs/perftest.c: Use from Glibc syscall numbers.
[catacomb] / symm / seal.c
CommitLineData
8dd8c294 1/* -*-c-*-
2 *
8dd8c294 3 * The SEAL pseudo-random function family
4 *
5 * (c) 2000 Straylight/Edgeware
6 */
7
45c0fd36 8/*----- Licensing notice --------------------------------------------------*
8dd8c294 9 *
10 * This file is part of Catacomb.
11 *
12 * Catacomb is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU Library General Public License as
14 * published by the Free Software Foundation; either version 2 of the
15 * License, or (at your option) any later version.
45c0fd36 16 *
8dd8c294 17 * Catacomb is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU Library General Public License for more details.
45c0fd36 21 *
8dd8c294 22 * You should have received a copy of the GNU Library General Public
23 * License along with Catacomb; if not, write to the Free
24 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
25 * MA 02111-1307, USA.
26 */
27
8dd8c294 28/*----- Header files ------------------------------------------------------*/
29
30#include <assert.h>
31#include <stdarg.h>
32#include <stdio.h>
33
34#include <mLib/bits.h>
35
36#include "arena.h"
37#include "gcipher.h"
38#include "grand.h"
39#include "paranoia.h"
40#include "seal.h"
41#include "sha.h"
42
43/*----- Global variables --------------------------------------------------*/
44
45const octet seal_keysz[] = { KSZ_ANY, SHA_HASHSZ };
46
47/*----- Main code ---------------------------------------------------------*/
48
49/* --- @gamma@ --- *
50 *
51 * Arguments: @uint32 *p@ = output table
52 * @size_t sz@ = size of the output table
53 * @const void *k@ = pointer to key material
54 * @unsigned i@ = integer offset
55 *
56 * Returns: ---
57 *
58 * Use: Initializes a SEAL key table.
59 */
60
aa02ed36 61static void sealgamma(uint32 *p, size_t sz, const void *k, unsigned i)
8dd8c294 62{
63 uint32 buf[80] = { 0 };
64 const octet *kk = k;
dd95e85d
MW
65 uint32 a, aa = LOAD32(kk);
66 uint32 b, bb = LOAD32(kk + 4);
67 uint32 c, cc = LOAD32(kk + 8);
68 uint32 d, dd = LOAD32(kk + 12);
69 uint32 e, ee = LOAD32(kk + 16);
70
71 unsigned skip = i%5;
72 uint32 x;
73 int j;
8dd8c294 74
8dd8c294 75 i /= 5;
76
77 /* --- While there's hashing to do, do hashing --- */
78
79 while (sz) {
dd95e85d 80 a = aa, b = bb, c = cc, d = dd, e = ee;
8dd8c294 81
82 /* --- Initialize and expand the buffer --- */
83
84 buf[0] = i++;
85
86 for (j = 16; j < 80; j++) {
dd95e85d 87 x = buf[j - 3] ^ buf[j - 8] ^ buf[j - 14] ^ buf[j - 16];
8dd8c294 88 buf[j] = ROL32(x, 1);
89 }
90
91 /* --- Definitions for round functions --- */
92
dd95e85d 93#define F(x, y, z) (((x)&(y)) | (~(x)&(z)))
8dd8c294 94#define G(x, y, z) ((x) ^ (y) ^ (z))
dd95e85d 95#define H(x, y, z) (((x)&(y)) | ((x)&(z)) | ((y)&(z)))
8dd8c294 96
97#define T(v, w, x, y, z, i, f, k) do { \
98 uint32 _x; \
99 z = ROL32(v, 5) + f(w, x, y) + z + buf[i] + k; \
100 w = ROR32(w, 2); \
101 _x = v; v = z; z = y; y = x; x = w; w = _x; \
102} while (0)
103
104#define FF(v, w, x, y, z, i) T(v, w, x, y, z, i, F, 0x5a827999)
105#define GG(v, w, x, y, z, i) T(v, w, x, y, z, i, G, 0x6ed9eba1)
106#define HH(v, w, x, y, z, i) T(v, w, x, y, z, i, H, 0x8f1bbcdc)
107#define II(v, w, x, y, z, i) T(v, w, x, y, z, i, G, 0xca62c1d6)
108
109 /* --- The main compression function --- *
110 *
111 * Since this isn't doing bulk hashing, do it the easy way.
112 */
113
dd95e85d
MW
114 for (j = 0; j < 20; j++) FF(a, b, c, d, e, j);
115 for (j = 20; j < 40; j++) GG(a, b, c, d, e, j);
116 for (j = 40; j < 60; j++) HH(a, b, c, d, e, j);
117 for (j = 60; j < 80; j++) II(a, b, c, d, e, j);
8dd8c294 118
119 /* --- Do the chaining at the end --- */
120
121 a += aa; b += bb; c += cc; d += dd; e += ee;
122
123 /* --- Write to the output buffer --- */
124
125 switch (skip) {
dd95e85d
MW
126 case 0: if (sz) { *p++ = a; sz--; }
127 case 1: if (sz) { *p++ = b; sz--; }
128 case 2: if (sz) { *p++ = c; sz--; }
129 case 3: if (sz) { *p++ = d; sz--; }
130 case 4: if (sz) { *p++ = e; sz--; }
131 skip = 0;
8dd8c294 132 }
45c0fd36 133 }
8dd8c294 134}
135
136/* --- @seal_initkey@ --- *
137 *
138 * Arguments: @seal_key *k@ = pointer to key block
139 * @const void *buf@ = pointer to key material
140 * @size_t sz@ = size of the key material
141 *
142 * Returns: ---
143 *
144 * Use: Initializes a SEAL key block. The key material may be any
145 * size, but if it's not 20 bytes long it's passed to SHA for
146 * hashing first.
147 */
148
149void seal_initkey(seal_key *k, const void *buf, size_t sz)
150{
dd95e85d
MW
151 sha_ctx h;
152
8dd8c294 153 /* --- Hash the key if it's the wrong size --- */
154
dd95e85d
MW
155 if (sz == SHA_HASHSZ) memcpy(k->k, buf, sizeof(k->k));
156 else { sha_init(&h); sha_hash(&h, buf, sz); sha_done(&h, k->k); }
8dd8c294 157
158 /* --- Expand the key to fit the various tables --- */
159
aa02ed36
MW
160 sealgamma(k->t, 512, k->k, 0);
161 sealgamma(k->s, 256, k->k, 0x1000);
162 sealgamma(k->r, SEAL_R, k->k, 0x2000);
8dd8c294 163}
164
165/* --- @seal_reset@ --- *
166 *
167 * Arguments: @seal_ctx *c@ = pointer to a SEAL context
168 *
169 * Returns: ---
170 *
171 * Use: Resets the context so that more data can be extracted from
172 * it.
173 */
174
175static void seal_reset(seal_ctx *c)
176{
177 seal_key *k = c->k;
178 uint32 n = c->n;
179 uint32 A, B, C, D;
180 unsigned p;
181
182 /* --- Initialize the new chaining variables --- */
183
184 if (c->l >= SEAL_R) {
aa02ed36 185 sealgamma(c->rbuf, SEAL_R, k->k, c->ri);
8dd8c294 186 c->ri += SEAL_R;
187 c->l = 0;
188 c->r = c->rbuf;
189 }
190
191 A = n ^ c->r[0];
45c0fd36 192 B = ROR32(n, 8) ^ c->r[1];
8dd8c294 193 C = ROR32(n, 16) ^ c->r[2];
194 D = ROR32(n, 24) ^ c->r[3];
195 c->l += 4;
196 c->r += 4;
197
198 /* --- Ensure that everything is sufficiently diffused --- */
199
dd95e85d
MW
200 p = A&0x7fc; B += k->t[p >> 2]; A = ROR32(A, 9);
201 p = B&0x7fc; C += k->t[p >> 2]; B = ROR32(B, 9);
202 p = C&0x7fc; D += k->t[p >> 2]; C = ROR32(C, 9);
203 p = D&0x7fc; A += k->t[p >> 2]; D = ROR32(D, 9);
204 p = A&0x7fc; B += k->t[p >> 2]; A = ROR32(A, 9);
205 p = B&0x7fc; C += k->t[p >> 2]; B = ROR32(B, 9);
206 p = C&0x7fc; D += k->t[p >> 2]; C = ROR32(C, 9);
207 p = D&0x7fc; A += k->t[p >> 2]; D = ROR32(D, 9);
8dd8c294 208
209 /* --- Write out some context --- */
210
211 c->n1 = D; c->n2 = B; c->n3 = A; c->n4 = C;
212
213 /* --- Diffuse some more --- */
214
dd95e85d
MW
215 p = A&0x7fc; B += k->t[p >> 2]; A = ROR32(A, 9);
216 p = B&0x7fc; C += k->t[p >> 2]; B = ROR32(B, 9);
217 p = C&0x7fc; D += k->t[p >> 2]; C = ROR32(C, 9);
218 p = D&0x7fc; A += k->t[p >> 2]; D = ROR32(D, 9);
8dd8c294 219
220 /* --- Write out the magic numbers --- */
221
222 c->a = A; c->b = B; c->c = C; c->d = D;
223 c->i = 0;
224}
225
226/* --- @seal_initctx@ --- *
227 *
228 * Arguments: @seal_ctx *c@ = pointer to a SEAL context
229 * @seal_key *k@ = pointer to a SEAL key
230 * @uint32 n@ = integer sequence number
231 *
232 * Returns: ---
233 *
234 * Use: Initializes a SEAL context which can be used for random
235 * number generation or whatever.
236 */
237
238void seal_initctx(seal_ctx *c, seal_key *k, uint32 n)
239{
240 c->k = k;
241 c->n = n;
242 c->l = 0;
243 c->r = k->r;
244 c->ri = 0x2000 + SEAL_R;
dd95e85d 245 c->off = 16;
8dd8c294 246 seal_reset(c);
247}
248
249/* --- @seal_encrypt@ --- *
250 *
251 * Arguments: @seal_ctx *c@ = pointer to a SEAL context
252 * @const void *src@ = pointer to source data
253 * @void *dest@ = pointer to destination data
254 * @size_t sz@ = size of the data
255 *
256 * Returns: ---
257 *
258 * Use: Encrypts a block of data using SEAL. If @src@ is zero,
259 * @dest@ is filled with SEAL output. If @dest@ is zero, the
260 * SEAL generator is just spun around for a bit. This shouldn't
261 * be necessary, because SEAL isn't RC4.
262 */
263
264void seal_encrypt(seal_ctx *c, const void *src, void *dest, size_t sz)
265{
dd95e85d 266 seal_key *k = c->k;
8dd8c294 267 const octet *s = src;
dd95e85d
MW
268 octet *d = dest, *p;
269 uint32 A = c->a, B = c->b, C = c->c, D = c->d;
270 uint32 n1 = c->n1, n2 = c->n2, n3 = c->n3, n4 = c->n4;
271 uint32 aa, bb, cc, dd;
272 unsigned i, j;
273 unsigned P, Q;
8dd8c294 274
275 /* --- Expect a big dollop of bytes --- */
276
dd95e85d
MW
277 if (sz > 16 - c->off) {
278 j = c->i;
279
280 /* --- Drain the buffer first --- */
281
282 if (c->off < 16) {
283 p = c->buf + c->off; sz -= 16 - c->off;
284 if (!d) /* nothing to do* */;
285 else if (!s) memcpy(d, p, 16 - c->off);
286 else for (i = c->off; i < 16; i++) *d++ = *s++ ^ *p++;
8dd8c294 287 }
288
289 /* --- Main sequence --- */
290
291 for (;;) {
8dd8c294 292
293 /* --- Reset if we've run out of steam on this iteration --- */
294
295 if (j == 256) {
296 seal_reset(c);
45c0fd36 297 A = c->a, B = c->b, C = c->c, D = c->d;
8dd8c294 298 n1 = c->n1, n2 = c->n2, n3 = c->n3, n4 = c->n4;
299 j = 0;
300 }
301
302 /* --- Make some new numbers --- */
303
dd95e85d
MW
304 P = A&0x7fc; B += k->t[P >> 2]; A = ROR32(A, 9); B ^= A;
305 Q = B&0x7fc; C ^= k->t[Q >> 2]; B = ROR32(B, 9); C += B;
306 P = (P + C)&0x7fc; D += k->t[P >> 2]; C = ROR32(C, 9); D ^= C;
307 Q = (Q + D)&0x7fc; A ^= k->t[Q >> 2]; D = ROR32(D, 9); A += D;
308 P = (P + A)&0x7fc; B ^= k->t[P >> 2]; A = ROR32(A, 9);
309 Q = (Q + B)&0x7fc; C += k->t[Q >> 2]; B = ROR32(B, 9);
310 P = (P + C)&0x7fc; D ^= k->t[P >> 2]; C = ROR32(C, 9);
311 Q = (Q + D)&0x7fc; A += k->t[Q >> 2]; D = ROR32(D, 9);
45c0fd36 312
8dd8c294 313 /* --- Remember the output and set up the next round --- */
314
dd95e85d
MW
315 aa = B + k->s[j + 0]; bb = C ^ k->s[j + 1];
316 cc = D + k->s[j + 2]; dd = A ^ k->s[j + 3];
8dd8c294 317 j += 4;
318
dd95e85d
MW
319 if (j&4) { A += n1; B += n2; C ^= n1; D ^= n2; }
320 else { A += n3; B += n4; C ^= n3; D ^= n4; }
8dd8c294 321
322 /* --- Bail out here if we need to do buffering --- */
323
dd95e85d 324 if (sz < 16) break;
8dd8c294 325
326 /* --- Write the next 16 bytes --- */
327
dd95e85d
MW
328 if (!d) /* nothing to do */;
329 else {
8dd8c294 330 if (s) {
dd95e85d
MW
331 aa ^= LOAD32_L(s + 0); bb ^= LOAD32_L(s + 4);
332 cc ^= LOAD32_L(s + 8); dd ^= LOAD32_L(s + 12);
8dd8c294 333 s += 16;
334 }
dd95e85d
MW
335 STORE32_L(d + 0, aa); STORE32_L(d + 4, bb);
336 STORE32_L(d + 8, cc); STORE32_L(d + 12, dd);
8dd8c294 337 d += 16;
338 }
339 sz -= 16;
340 }
341
dd95e85d 342 /* --- Write the new buffer --- */
8dd8c294 343
dd95e85d
MW
344 STORE32_L(c->buf + 0, aa);
345 STORE32_L(c->buf + 4, bb);
346 STORE32_L(c->buf + 8, cc);
347 STORE32_L(c->buf + 12, dd);
348 c->off = 0;
8dd8c294 349
350 c->a = A; c->b = B; c->c = C; c->d = D;
351 c->i = j;
352 }
353
dd95e85d 354 /* --- Deal with the rest from the buffer --- */
8dd8c294 355
356 if (sz) {
dd95e85d
MW
357 p = c->buf + c->off; c->off += sz;
358 if (!d) /* nothing to do* */;
359 else if (!s) memcpy(d, p, sz);
360 else for (i = 0; i < sz; i++) *d++ = *s++ ^ *p++;
8dd8c294 361 }
362}
363
364/*----- Generic cipher interface ------------------------------------------*/
365
366typedef struct gctx {
367 gcipher c;
368 seal_key k;
369 seal_ctx cc;
370} gctx;
371
372static const gcipher_ops gops;
373
374static gcipher *ginit(const void *k, size_t sz)
375{
376 gctx *g = S_CREATE(gctx);
377 g->c.ops = &gops;
378 seal_initkey(&g->k, k, sz);
379 seal_initctx(&g->cc, &g->k, 0);
380 return (&g->c);
381}
382
383static void gencrypt(gcipher *c, const void *s, void *t, size_t sz)
dd95e85d 384 { gctx *g = (gctx *)c; seal_encrypt(&g->cc, s, t, sz); }
8dd8c294 385
386static void gsetiv(gcipher *c, const void *iv)
387{
388 gctx *g = (gctx *)c;
3af7e537
MW
389 const octet *ivp = iv;
390 seal_initctx(&g->cc, &g->k, LOAD32(ivp));
8dd8c294 391}
392
393static void gdestroy(gcipher *c)
dd95e85d 394 { gctx *g = (gctx *)c; BURN(*g); S_DESTROY(g); }
8dd8c294 395
396static const gcipher_ops gops = {
397 &seal,
398 gencrypt, gencrypt, gdestroy, gsetiv, 0
399};
400
401const gccipher seal = {
3af7e537 402 "seal", seal_keysz, 4,
8dd8c294 403 ginit
404};
405
406/*----- Generic random number generator interface -------------------------*/
407
408typedef struct grctx {
409 grand r;
410 seal_key k;
411 seal_ctx cc;
412} grctx;
413
414static void grdestroy(grand *r)
dd95e85d 415 { grctx *g = (grctx *)r; BURN(*g); S_DESTROY(g); }
8dd8c294 416
417static int grmisc(grand *r, unsigned op, ...)
418{
419 grctx *g = (grctx *)r;
420 va_list ap;
421 int rc = 0;
422 va_start(ap, op);
423
424 switch (op) {
425 case GRAND_CHECK:
426 switch (va_arg(ap, unsigned)) {
427 case GRAND_CHECK:
428 case GRAND_SEEDINT:
429 case GRAND_SEEDUINT32:
430 case GRAND_SEEDBLOCK:
431 case GRAND_SEEDRAND:
432 rc = 1;
433 break;
434 default:
435 rc = 0;
436 break;
437 }
438 break;
439 case GRAND_SEEDINT:
440 seal_initctx(&g->cc, &g->k, va_arg(ap, int));
441 break;
442 case GRAND_SEEDUINT32:
443 seal_initctx(&g->cc, &g->k, va_arg(ap, uint32));
444 break;
445 case GRAND_SEEDBLOCK: {
446 const void *p = va_arg(ap, const void *);
447 size_t sz = va_arg(ap, size_t);
448 uint32 n;
449 if (sz >= 4)
450 n = LOAD32_L(p);
451 else {
452 octet buf[4] = { 0 };
453 memcpy(buf, p, sz);
454 n = LOAD32_L(p);
455 }
456 seal_initctx(&g->cc, &g->k, n);
457 } break;
458 case GRAND_SEEDRAND: {
459 grand *rr = va_arg(ap, grand *);
460 seal_initctx(&g->cc, &g->k, rr->ops->word(rr));
461 } break;
462 default:
463 GRAND_BADOP;
464 break;
465 }
466
467 va_end(ap);
468 return (rc);
469}
470
471static octet grbyte(grand *r)
472{
473 grctx *g = (grctx *)r;
474 octet o;
475 seal_encrypt(&g->cc, 0, &o, 1);
476 return (o);
477}
478
479static uint32 grword(grand *r)
480{
481 grctx *g = (grctx *)r;
482 octet b[4];
483 seal_encrypt(&g->cc, 0, b, 4);
484 return (LOAD32(b));
485}
486
487static void grfill(grand *r, void *p, size_t sz)
dd95e85d 488 { grctx *g = (grctx *)r; seal_encrypt(&g->cc, 0, p, sz); }
8dd8c294 489
490static const grand_ops grops = {
491 "seal",
492 GRAND_CRYPTO, 0,
493 grmisc, grdestroy,
44ff6c11 494 grword, grbyte, grword, grand_defaultrange, grfill
8dd8c294 495};
496
497/* --- @seal_rand@ --- *
498 *
499 * Arguments: @const void *k@ = pointer to key material
500 * @size_t sz@ = size of key material
501 * @uint32 n@ = sequence number
502 *
503 * Returns: Pointer to generic random number generator interface.
504 *
505 * Use: Creates a random number interface wrapper around a SEAL
506 * pseudorandom function.
507 */
508
509grand *seal_rand(const void *k, size_t sz, uint32 n)
510{
511 grctx *g = S_CREATE(grctx);
512 g->r.ops = &grops;
513 seal_initkey(&g->k, k, sz);
514 seal_initctx(&g->cc, &g->k, n);
515 return (&g->r);
516}
517
518/*----- Test rig ----------------------------------------------------------*/
519
520#ifdef TEST_RIG
521
522#include <string.h>
523
141c1284 524#include <mLib/macros.h>
8dd8c294 525#include <mLib/testrig.h>
526
527static int verify(dstr *v)
528{
529 seal_key k;
530 seal_ctx c;
531 uint32 n = *(uint32 *)v[1].buf;
532 dstr d = DSTR_INIT;
533 dstr z = DSTR_INIT;
534 int i;
535 int ok = 1;
536
537 DENSURE(&d, v[2].len);
538 DENSURE(&z, v[2].len);
539 memset(z.buf, 0, v[2].len);
540 z.len = d.len = v[2].len;
541 seal_initkey(&k, v[0].buf, v[0].len);
542
543 for (i = 0; i < v[2].len; i++) {
544 seal_initctx(&c, &k, n);
545 seal_encrypt(&c, 0, d.buf, i);
546 seal_encrypt(&c, z.buf, d.buf + i, d.len - i);
141c1284 547 if (MEMCMP(d.buf, !=, v[2].buf, d.len)) {
8dd8c294 548 ok = 0;
549 printf("*** seal failure\n");
550 printf("*** k = "); type_hex.dump(&v[0], stdout); putchar('\n');
551 printf("*** n = %08lx\n", (unsigned long)n);
552 printf("*** i = %i\n", i);
553 printf("*** expected = "); type_hex.dump(&v[2], stdout); putchar('\n');
554 printf("*** computed = "); type_hex.dump(&d, stdout); putchar('\n');
555 }
556 }
557
558 dstr_destroy(&d);
559 dstr_destroy(&z);
560
561 return (ok);
562}
563
564static test_chunk defs[] = {
565 { "seal", verify, { &type_hex, &type_uint32, &type_hex, 0 } },
566 { 0, 0, { 0 } }
567};
568
569int main(int argc, char *argv[])
570{
0f00dc4c 571 test_run(argc, argv, defs, SRCDIR"/t/seal");
8dd8c294 572 return (0);
573}
574
575#endif
576
577/*----- That's all, folks -------------------------------------------------*/