gcm.c 14.2 KB
Newer Older
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/* gcm.h
 *
 * Galois counter mode, specified by NIST,
 * http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf
 *
 */

/* NOTE: Tentative interface, subject to change. No effort will be
   made to avoid incompatible changes. */

/* nettle, low-level cryptographics library
 *
 * Copyright (C) 2011 Niels Möller
 * Copyright (C) 2011 Katholieke Universiteit Leuven
 * 
 * Contributed by Nikos Mavrogiannopoulos
 *
 * The nettle library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or (at your
 * option) any later version.
 * 
 * The nettle library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
 * License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with the nettle library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA.
 */

#if HAVE_CONFIG_H
# include "config.h"
#endif

#include <assert.h>
#include <stdlib.h>
#include <string.h>

#include "gcm.h"

#include "memxor.h"
#include "nettle-internal.h"
#include "macros.h"

48
#define GHASH_POLYNOMIAL 0xE1UL
49

50
static void
51
gcm_gf_add (union gcm_block *r, const union gcm_block *x, const union gcm_block *y)
52
{
53
54
  r->w[0] = x->w[0] ^ y->w[0];
  r->w[1] = x->w[1] ^ y->w[1];
55
#if SIZEOF_LONG == 4
56
57
  r->w[2] = x->w[2] ^ y->w[2];
  r->w[3] = x->w[3] ^ y->w[3];
58
59
#endif      
}
60
61
/* Multiplication by 010...0; a big-endian shift right. If the bit
   shifted out is one, the defining polynomial is added to cancel it
62
   out. */
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
63
static void
64
gcm_gf_shift (union gcm_block *x)
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
65
{
66
  unsigned long *w = x->w;
67
  long mask;
68

69
70
71
  /* Shift uses big-endian representation. */
#if WORDS_BIGENDIAN
# if SIZEOF_LONG == 4
72
  mask = - (w[3] & 1);
73
74
75
  w[3] = (w[3] >> 1) | ((w[2] & 1) << 31);
  w[2] = (w[2] >> 1) | ((w[1] & 1) << 31);
  w[1] = (w[1] >> 1) | ((w[0] & 1) << 31);
76
  w[0] = (w[0] >> 1) ^ (mask & (GHASH_POLYNOMIAL << 24)); 
77
# elif SIZEOF_LONG == 8
78
  mask = - (w[1] & 1);
79
  w[1] = (w[1] >> 1) | ((w[0] & 1) << 63);
80
  w[0] = (w[0] >> 1) ^ (mask & (GHASH_POLYNOMIAL << 56));
81
82
83
84
85
86
87
# else
#  error Unsupported word size. */
#endif
#else /* ! WORDS_BIGENDIAN */
# if SIZEOF_LONG == 4
#define RSHIFT_WORD(x) \
  ((((x) & 0xfefefefeUL) >> 1) \
88
   | (((x) & 0x00010101) << 15))
89
  mask = - ((w[3] >> 24) & 1);
90
91
92
  w[3] = RSHIFT_WORD(w[3]) | ((w[2] >> 17) & 0x80);
  w[2] = RSHIFT_WORD(w[2]) | ((w[1] >> 17) & 0x80);
  w[1] = RSHIFT_WORD(w[1]) | ((w[0] >> 17) & 0x80);
93
  w[0] = RSHIFT_WORD(w[0]) ^ (mask & GHASH_POLYNOMIAL);
94
95
96
# elif SIZEOF_LONG == 8
#define RSHIFT_WORD(x) \
  ((((x) & 0xfefefefefefefefeUL) >> 1) \
97
   | (((x) & 0x0001010101010101UL) << 15))
98
  mask = - ((w[1] >> 56) & 1);
99
  w[1] = RSHIFT_WORD(w[1]) | ((w[0] >> 49) & 0x80);
100
  w[0] = RSHIFT_WORD(w[0]) ^ (mask & GHASH_POLYNOMIAL);
101
102
103
# else
#  error Unsupported word size. */
# endif
104
# undef RSHIFT_WORD
105
#endif /* ! WORDS_BIGENDIAN */
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
106
107
}

108
109
110
/* Sets r <- x * y mod r, using the plain bitwise algorithm from the
   specification. y may be shorter than a full block, missing bytes
   are assumed zero. */
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
111
static void
112
gcm_gf_mul (union gcm_block *r, const union gcm_block *x, unsigned yn, const uint8_t *y)
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
113
{
114
115
  union gcm_block V;
  union gcm_block Z;
116
  unsigned i;
117

118
119
  memcpy(V.b, x, sizeof(V));
  memset(Z.b, 0, sizeof(Z));
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
120

121
  for (i = 0; i < yn; i++)
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
122
    {
123
124
125
126
127
      uint8_t b = y[i];
      unsigned j;
      for (j = 0; j < 8; j++, b <<= 1)
	{
	  if (b & 0x80)
128
	    gcm_gf_add(&Z, &Z, &V);
129
	  
130
	  gcm_gf_shift(&V);
131
	}
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
132
    }
133
  memcpy (r->b, Z.b, sizeof(Z));
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
}

#if GCM_TABLE_BITS
# if WORDS_BIGENDIAN
#  define W(left,right) (0x##left##right)
# else
#  define W(left,right) (0x##right##left)
# endif

#if GCM_TABLE_BITS == 4
static const uint16_t
shift_table[0x10] = {
  W(00,00),W(1c,20),W(38,40),W(24,60),W(70,80),W(6c,a0),W(48,c0),W(54,e0),
  W(e1,00),W(fd,20),W(d9,40),W(c5,60),W(91,80),W(8d,a0),W(a9,c0),W(b5,e0),
};
#elif GCM_TABLE_BITS == 8
static const uint16_t
shift_table[0x100] = {
  W(00,00),W(01,c2),W(03,84),W(02,46),W(07,08),W(06,ca),W(04,8c),W(05,4e),
  W(0e,10),W(0f,d2),W(0d,94),W(0c,56),W(09,18),W(08,da),W(0a,9c),W(0b,5e),
  W(1c,20),W(1d,e2),W(1f,a4),W(1e,66),W(1b,28),W(1a,ea),W(18,ac),W(19,6e),
  W(12,30),W(13,f2),W(11,b4),W(10,76),W(15,38),W(14,fa),W(16,bc),W(17,7e),
  W(38,40),W(39,82),W(3b,c4),W(3a,06),W(3f,48),W(3e,8a),W(3c,cc),W(3d,0e),
  W(36,50),W(37,92),W(35,d4),W(34,16),W(31,58),W(30,9a),W(32,dc),W(33,1e),
  W(24,60),W(25,a2),W(27,e4),W(26,26),W(23,68),W(22,aa),W(20,ec),W(21,2e),
  W(2a,70),W(2b,b2),W(29,f4),W(28,36),W(2d,78),W(2c,ba),W(2e,fc),W(2f,3e),
  W(70,80),W(71,42),W(73,04),W(72,c6),W(77,88),W(76,4a),W(74,0c),W(75,ce),
  W(7e,90),W(7f,52),W(7d,14),W(7c,d6),W(79,98),W(78,5a),W(7a,1c),W(7b,de),
  W(6c,a0),W(6d,62),W(6f,24),W(6e,e6),W(6b,a8),W(6a,6a),W(68,2c),W(69,ee),
  W(62,b0),W(63,72),W(61,34),W(60,f6),W(65,b8),W(64,7a),W(66,3c),W(67,fe),
  W(48,c0),W(49,02),W(4b,44),W(4a,86),W(4f,c8),W(4e,0a),W(4c,4c),W(4d,8e),
  W(46,d0),W(47,12),W(45,54),W(44,96),W(41,d8),W(40,1a),W(42,5c),W(43,9e),
  W(54,e0),W(55,22),W(57,64),W(56,a6),W(53,e8),W(52,2a),W(50,6c),W(51,ae),
  W(5a,f0),W(5b,32),W(59,74),W(58,b6),W(5d,f8),W(5c,3a),W(5e,7c),W(5f,be),
  W(e1,00),W(e0,c2),W(e2,84),W(e3,46),W(e6,08),W(e7,ca),W(e5,8c),W(e4,4e),
  W(ef,10),W(ee,d2),W(ec,94),W(ed,56),W(e8,18),W(e9,da),W(eb,9c),W(ea,5e),
  W(fd,20),W(fc,e2),W(fe,a4),W(ff,66),W(fa,28),W(fb,ea),W(f9,ac),W(f8,6e),
  W(f3,30),W(f2,f2),W(f0,b4),W(f1,76),W(f4,38),W(f5,fa),W(f7,bc),W(f6,7e),
  W(d9,40),W(d8,82),W(da,c4),W(db,06),W(de,48),W(df,8a),W(dd,cc),W(dc,0e),
  W(d7,50),W(d6,92),W(d4,d4),W(d5,16),W(d0,58),W(d1,9a),W(d3,dc),W(d2,1e),
  W(c5,60),W(c4,a2),W(c6,e4),W(c7,26),W(c2,68),W(c3,aa),W(c1,ec),W(c0,2e),
  W(cb,70),W(ca,b2),W(c8,f4),W(c9,36),W(cc,78),W(cd,ba),W(cf,fc),W(ce,3e),
  W(91,80),W(90,42),W(92,04),W(93,c6),W(96,88),W(97,4a),W(95,0c),W(94,ce),
  W(9f,90),W(9e,52),W(9c,14),W(9d,d6),W(98,98),W(99,5a),W(9b,1c),W(9a,de),
  W(8d,a0),W(8c,62),W(8e,24),W(8f,e6),W(8a,a8),W(8b,6a),W(89,2c),W(88,ee),
  W(83,b0),W(82,72),W(80,34),W(81,f6),W(84,b8),W(85,7a),W(87,3c),W(86,fe),
  W(a9,c0),W(a8,02),W(aa,44),W(ab,86),W(ae,c8),W(af,0a),W(ad,4c),W(ac,8e),
  W(a7,d0),W(a6,12),W(a4,54),W(a5,96),W(a0,d8),W(a1,1a),W(a3,5c),W(a2,9e),
  W(b5,e0),W(b4,22),W(b6,64),W(b7,a6),W(b2,e8),W(b3,2a),W(b1,6c),W(b0,ae),
  W(bb,f0),W(ba,32),W(b8,74),W(b9,b6),W(bc,f8),W(bd,3a),W(bf,7c),W(be,be),
};
#else
#error Unsupported table size
#endif
#undef W

/* Like gcm_rightshift, but shifts GCM_TABLE_BITS steps. */
#if GCM_TABLE_BITS == 4

static void
194
gcm_gf_shift_chunk(union gcm_block *x)
195
{
196
  unsigned long *w = x->w;
197
198
199
200
201
202
203
204
205
206
207
  unsigned long reduce;

  /* Shift uses big-endian representation. */
#if WORDS_BIGENDIAN
# if SIZEOF_LONG == 4
  reduce = shift_table[w[3] & 0xf];
  w[3] = (w[3] >> 4) | ((w[2] & 0xf) << 28);
  w[2] = (w[2] >> 4) | ((w[1] & 0xf) << 28);
  w[1] = (w[1] >> 4) | ((w[0] & 0xf) << 28);
  w[0] = (w[0] >> 4) ^ (reduce << 16);
# elif SIZEOF_LONG == 8
208
209
  reduce = shift_table[w[1] & 0xf];
  w[1] = (w[1] >> 4) | ((w[0] & 0xf) << 60);
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
  w[0] = (w[0] >> 4) ^ (reduce << 48);
# else
#  error Unsupported word size. */
#endif
#else /* ! WORDS_BIGENDIAN */
# if SIZEOF_LONG == 4
#define RSHIFT_WORD(x) \
  ((((x) & 0xf0f0f0f0UL) >> 4)			\
   | (((x) & 0x000f0f0f) << 12))
  reduce = shift_table[(w[3] >> 24) & 0xf];
  w[3] = RSHIFT_WORD(w[3]) | ((w[2] >> 20) & 0xf0);
  w[2] = RSHIFT_WORD(w[2]) | ((w[1] >> 20) & 0xf0);
  w[1] = RSHIFT_WORD(w[1]) | ((w[0] >> 20) & 0xf0);
  w[0] = RSHIFT_WORD(w[0]) ^ reduce;
# elif SIZEOF_LONG == 8
#define RSHIFT_WORD(x) \
  ((((x) & 0xf0f0f0f0f0f0f0f0UL) >> 4) \
   | (((x) & 0x000f0f0f0f0f0f0fUL) << 12))
  reduce = shift_table[(w[1] >> 56) & 0xf];
  w[1] = RSHIFT_WORD(w[1]) | ((w[0] >> 52) & 0xf0);
  w[0] = RSHIFT_WORD(w[0]) ^ reduce;
# else
#  error Unsupported word size. */
# endif
# undef RSHIFT_WORD
#endif /* ! WORDS_BIGENDIAN */
}

static void
239
gcm_gf_mul_chunk (union gcm_block *x, const union gcm_block *h, const union gcm_block *table)
240
{
241
  union gcm_block Z;
242
243
  unsigned i;

244
  memset(Z.b, 0, sizeof(Z));
245
246
247

  for (i = GCM_BLOCK_SIZE; i-- > 0;)
    {
248
      uint8_t b = x->b[i];
249

250
251
252
253
      gcm_gf_shift_chunk(&Z);
      gcm_gf_add(&Z, &Z, &table[b & 0xf]);
      gcm_gf_shift_chunk(&Z);
      gcm_gf_add(&Z, &Z, &table[b >> 4]);
254
    }
255
  memcpy (x->b, Z.b, sizeof(Z));
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
256
}
257
258
#elif GCM_TABLE_BITS == 8
static void
259
gcm_gf_shift_chunk(union gcm_block *x)
260
{
261
  unsigned long *w = x->w;
262
263
264
265
266
267
268
269
270
271
272
  unsigned long reduce;

  /* Shift uses big-endian representation. */
#if WORDS_BIGENDIAN
# if SIZEOF_LONG == 4
  reduce = shift_table[w[3] & 0xff];
  w[3] = (w[3] >> 8) | ((w[2] & 0xff) << 24);
  w[2] = (w[2] >> 8) | ((w[1] & 0xff) << 24);
  w[1] = (w[1] >> 8) | ((w[0] & 0xff) << 24);
  w[0] = (w[0] >> 8) ^ (reduce << 16);
# elif SIZEOF_LONG == 8
273
  reduce = shift_table[w[1] & 0xff];
274
275
276
277
278
279
280
  w[1] = (w[1] >> 8) | ((w[0] & 0xff) << 56);
  w[0] = (w[0] >> 8) ^ (reduce << 48);
# else
#  error Unsupported word size. */
#endif
#else /* ! WORDS_BIGENDIAN */
# if SIZEOF_LONG == 4
281
  reduce = shift_table[(w[3] >> 24) & 0xff];
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
  w[3] = (w[3] << 8) | (w[2] >> 24);
  w[2] = (w[2] << 8) | (w[1] >> 24);
  w[1] = (w[1] << 8) | (w[0] >> 24);
  w[0] = (w[0] << 8) ^ reduce;
# elif SIZEOF_LONG == 8
  reduce = shift_table[(w[1] >> 56) & 0xff];
  w[1] = (w[1] << 8) | (w[0] >> 56);
  w[0] = (w[0] << 8) ^ reduce;
# else
#  error Unsupported word size. */
# endif
#endif /* ! WORDS_BIGENDIAN */
}

static void
297
gcm_gf_mul_chunk (union gcm_block *x, const union gcm_block *h, const union gcm_block *table)
298
{
299
  union gcm_block Z;
300
301
  unsigned i;

302
  memcpy(Z.b, table[x->b[GCM_BLOCK_SIZE-1]].b, GCM_BLOCK_SIZE);
303

304
  for (i = GCM_BLOCK_SIZE-2; i > 0; i--)
305
    {
306
307
      gcm_gf_shift_chunk(&Z);
      gcm_gf_add(&Z, &Z, &table[x->b[i]]);
308
    }
309
310
  gcm_gf_shift_chunk(&Z);
  gcm_gf_add(x, &Z, &table[x->b[0]]);
311
312
313
314
315
}

#else /* GCM_TABLE_BITS != 8 */
#error Unsupported table size. 
#endif /* GCM_TABLE_BITS != 8 */
316
#endif /* GCM_TABLE_BITS */
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
317
318

/* Increment the rightmost 32 bits. */
319
#define INC32(block) INCREMENT(4, (block.b) + GCM_BLOCK_SIZE - 4)
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
320
321
322
323
324
325
326
327
328
329

/* Initialization of GCM.
 * @ctx: The context of GCM
 * @cipher: The context of the underlying block cipher
 * @f: The underlying cipher encryption function
 */
void
gcm_set_key(struct gcm_ctx *ctx,
	    void *cipher, nettle_crypt_func f)
{
330
331
  memset (ctx->h.b, 0, sizeof (ctx->h));
  f (cipher, GCM_BLOCK_SIZE, ctx->h.b, ctx->h.b);  /* H */
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
332
#if GCM_TABLE_BITS
333
334
335
#if GCM_TABLE_BITS == 4
  {
    unsigned i;
336
337
338
    for (i = 0; i < 0x10; i++)
      {
	uint8_t x = i << 4;
339
	gcm_gf_mul(&ctx->h_table[i], &ctx->h, 1, &x);
340
341
342
343
344
345
      }
  }
#elif GCM_TABLE_BITS == 8
  {
    unsigned i;
    for (i = 0; i < 0x100; i++)
346
      {
347
	uint8_t x = i;
348
	gcm_gf_mul(&ctx->h_table[i], &ctx->h, 1, &x);
349
350
351
      }
  }
#else
352
#error Unsupported table size
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
353
#endif
354
#endif /* GCM_TABLE_BITS */
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
355
356
357
358
359
360
361
362
363
364
365
366
}

/*
 * @length: The size of the iv (fixed for now to GCM_NONCE_SIZE)
 * @iv: The iv
 */
void
gcm_set_iv(struct gcm_ctx *ctx, unsigned length, const uint8_t* iv)
{
  /* FIXME: remove the iv size limitation */
  assert (length == GCM_IV_SIZE);

367
368
369
370
371
  memcpy (ctx->iv.b, iv, GCM_BLOCK_SIZE - 4);
  ctx->iv.b[GCM_BLOCK_SIZE - 4] = 0;
  ctx->iv.b[GCM_BLOCK_SIZE - 3] = 0;
  ctx->iv.b[GCM_BLOCK_SIZE - 2] = 0;
  ctx->iv.b[GCM_BLOCK_SIZE - 1] = 1;
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
372

373
  memcpy (ctx->ctr.b, ctx->iv.b, GCM_BLOCK_SIZE);
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
374
375
376
  INC32 (ctx->ctr);

  /* Reset the rest of the message-dependent state. */
377
  memset(ctx->x.b, 0, sizeof(ctx->x));
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
378
379
380
381
382
383
384
385
386
  ctx->auth_size = ctx->data_size = 0;
}

static void
gcm_hash(struct gcm_ctx *ctx, unsigned length, const uint8_t *data)
{
  for (; length >= GCM_BLOCK_SIZE;
       length -= GCM_BLOCK_SIZE, data += GCM_BLOCK_SIZE)
    {
387
      memxor (ctx->x.b, data, GCM_BLOCK_SIZE);
388
#if GCM_TABLE_BITS
389
      gcm_gf_mul_chunk (&ctx->x, &ctx->h, ctx->h_table);
390
#else
391
      gcm_gf_mul (&ctx->x, &ctx->x, GCM_BLOCK_SIZE, ctx->h.b);
392
#endif
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
393
394
395
    }
  if (length > 0)
    {
396
      memxor (ctx->x.b, data, length);
397
#if GCM_TABLE_BITS
398
      gcm_gf_mul_chunk (&ctx->x, &ctx->h, ctx->h_table);
399
#else
400
      gcm_gf_mul (&ctx->x, &ctx->x, GCM_BLOCK_SIZE, ctx->h.b);
401
#endif
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
    }
}

void
gcm_auth(struct gcm_ctx *ctx,
	 unsigned length, const uint8_t *data)
{
  assert(ctx->auth_size % GCM_BLOCK_SIZE == 0);
  assert(ctx->data_size % GCM_BLOCK_SIZE == 0);

  gcm_hash(ctx, length, data);

  ctx->auth_size += length;
}

static void
gcm_crypt(struct gcm_ctx *ctx, void *cipher, nettle_crypt_func *f,
	  unsigned length,
	   uint8_t *dst, const uint8_t *src)
{
  uint8_t buffer[GCM_BLOCK_SIZE];

  if (src != dst)
    {
      for (; length >= GCM_BLOCK_SIZE;
           (length -= GCM_BLOCK_SIZE,
	    src += GCM_BLOCK_SIZE, dst += GCM_BLOCK_SIZE))
        {
430
          f (cipher, GCM_BLOCK_SIZE, dst, ctx->ctr.b);
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
431
432
433
434
435
436
437
438
439
440
          memxor (dst, src, GCM_BLOCK_SIZE);
          INC32 (ctx->ctr);
        }
    }
  else
    {
      for (; length >= GCM_BLOCK_SIZE;
           (length -= GCM_BLOCK_SIZE,
	    src += GCM_BLOCK_SIZE, dst += GCM_BLOCK_SIZE))
        {
441
          f (cipher, GCM_BLOCK_SIZE, buffer, ctx->ctr.b);
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
442
443
444
445
446
447
448
          memxor3 (dst, src, buffer, GCM_BLOCK_SIZE);
          INC32 (ctx->ctr);
        }
    }
  if (length > 0)
    {
      /* A final partial block */
449
      f (cipher, GCM_BLOCK_SIZE, buffer, ctx->ctr.b);
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
      memxor3 (dst, src, buffer, length);
      INC32 (ctx->ctr);
    }
}

void
gcm_encrypt (struct gcm_ctx *ctx, void *cipher, nettle_crypt_func *f,
	     unsigned length,
             uint8_t *dst, const uint8_t *src)
{
  assert(ctx->data_size % GCM_BLOCK_SIZE == 0);

  gcm_crypt(ctx, cipher, f, length, dst, src);
  gcm_hash(ctx, length, dst);

  ctx->data_size += length;
}

void
gcm_decrypt(struct gcm_ctx *ctx, void *cipher, nettle_crypt_func *f,
	    unsigned length, uint8_t *dst, const uint8_t *src)
{
  assert(ctx->data_size % GCM_BLOCK_SIZE == 0);

  gcm_hash(ctx, length, src);
  gcm_crypt(ctx, cipher, f, length, dst, src);

  ctx->data_size += length;
}

void
gcm_digest(struct gcm_ctx *ctx, void *cipher, nettle_crypt_func *f,
	   unsigned length, uint8_t *digest)
{
  uint8_t buffer[GCM_BLOCK_SIZE];

  assert (length <= GCM_BLOCK_SIZE);

  ctx->data_size *= 8;
  ctx->auth_size *= 8;

  WRITE_UINT64 (buffer, ctx->auth_size);
  WRITE_UINT64 (buffer + 8, ctx->data_size);

  gcm_hash(ctx, GCM_BLOCK_SIZE, buffer);

496
497
  f (cipher, GCM_BLOCK_SIZE, buffer, ctx->iv.b);
  memxor3 (digest, ctx->x.b, buffer, length);
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
498
499
500

  return;
}