gcm.c 14.2 KB
Newer Older
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
1
2
3
4
5
/* gcm.h
 *
 * Galois counter mode, specified by NIST,
 * http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf
 *
6
7
 * See also the gcm paper at
 * http://www.cryptobarn.com/papers/gcm-spec.pdf.
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
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
48
49
 */

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

50
#define GHASH_POLYNOMIAL 0xE1UL
51

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

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

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

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

123
  for (i = 0; i < GCM_BLOCK_SIZE; i++)
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
124
    {
125
      uint8_t b = y->b[i];
126
127
128
129
      unsigned j;
      for (j = 0; j < 8; j++, b <<= 1)
	{
	  if (b & 0x80)
130
	    gcm_gf_add(&Z, &Z, &V);
131
	  
132
	  gcm_gf_shift(&V, &V);
133
	}
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
134
    }
135
  memcpy (x->b, Z.b, sizeof(Z));
136
}
137
#else /* GCM_TABLE_BITS != 0 */
138
139
140
141
142
143
144

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

145
# if GCM_TABLE_BITS == 4
146
147
148
149
150
151
152
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),
};

static void
153
gcm_gf_shift_4(union gcm_block *x)
154
{
155
  unsigned long *w = x->w;
156
157
158
159
160
161
162
163
164
165
166
  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
167
168
  reduce = shift_table[w[1] & 0xf];
  w[1] = (w[1] >> 4) | ((w[0] & 0xf) << 60);
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
194
195
196
197
  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
198
gcm_gf_mul (union gcm_block *x, const union gcm_block *table)
199
{
200
  union gcm_block Z;
201
202
  unsigned i;

203
  memset(Z.b, 0, sizeof(Z));
204
205
206

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

209
      gcm_gf_shift_4(&Z);
210
      gcm_gf_add(&Z, &Z, &table[b & 0xf]);
211
      gcm_gf_shift_4(&Z);
212
      gcm_gf_add(&Z, &Z, &table[b >> 4]);
213
    }
214
  memcpy (x->b, Z.b, sizeof(Z));
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
215
}
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
# 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),
};

253
static void
254
gcm_gf_shift_8(union gcm_block *x)
255
{
256
  unsigned long *w = x->w;
257
258
259
260
261
262
263
264
265
266
267
  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
268
  reduce = shift_table[w[1] & 0xff];
269
270
271
272
273
274
275
  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
276
  reduce = shift_table[(w[3] >> 24) & 0xff];
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
  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
292
gcm_gf_mul (union gcm_block *x, const union gcm_block *table)
293
{
294
  union gcm_block Z;
295
296
  unsigned i;

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

299
  for (i = GCM_BLOCK_SIZE-2; i > 0; i--)
300
    {
301
      gcm_gf_shift_8(&Z);
302
      gcm_gf_add(&Z, &Z, &table[x->b[i]]);
303
    }
304
  gcm_gf_shift_8(&Z);
305
  gcm_gf_add(x, &Z, &table[x->b[0]]);
306
307
}

308
309
310
311
312
313
# else /* GCM_TABLE_BITS != 8 */
#  error Unsupported table size. 
# endif /* GCM_TABLE_BITS != 8 */

#undef W

314
#endif /* GCM_TABLE_BITS */
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
315
316

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

/* Initialization of GCM.
 * @ctx: The context of GCM
 * @cipher: The context of the underlying block cipher
 * @f: The underlying cipher encryption function
 */
void
325
gcm_set_key(struct gcm_key *key,
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
326
327
	    void *cipher, nettle_crypt_func f)
{
328
329
330
331
332
  /* Middle element if GCM_TABLE_BITS > 0, otherwise the first
     element */
  unsigned i = (1<<GCM_TABLE_BITS)/2;

  /* H */  
333
334
  memset(key->h[0].b, 0, GCM_BLOCK_SIZE);
  f (cipher, GCM_BLOCK_SIZE, key->h[i].b, key->h[0].b);
335
  
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
336
#if GCM_TABLE_BITS
337
338
339
  /* Algorithm 3 from the gcm paper. First do powers of two, then do
     the rest by adding. */
  while (i /= 2)
340
    gcm_gf_shift(&key->h[i], &key->h[2*i]);
341
342
343
344
  for (i = 2; i < 1<<GCM_TABLE_BITS; i *= 2)
    {
      unsigned j;
      for (j = 1; j < i; j++)
345
	gcm_gf_add(&key->h[i+j], &key->h[i],&key->h[j]);
346
    }
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
347
348
349
350
351
352
353
354
355
356
357
358
359
#endif
}

/*
 * @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);

360
361
362
363
364
  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
365

366
  memcpy (ctx->ctr.b, ctx->iv.b, GCM_BLOCK_SIZE);
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
367
368
369
  INC32 (ctx->ctr);

  /* Reset the rest of the message-dependent state. */
370
  memset(ctx->x.b, 0, sizeof(ctx->x));
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
371
372
373
374
  ctx->auth_size = ctx->data_size = 0;
}

static void
375
376
gcm_hash(const struct gcm_key *key, union gcm_block *x,
	 unsigned length, const uint8_t *data)
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
377
378
379
380
{
  for (; length >= GCM_BLOCK_SIZE;
       length -= GCM_BLOCK_SIZE, data += GCM_BLOCK_SIZE)
    {
381
382
      memxor (x->b, data, GCM_BLOCK_SIZE);
      gcm_gf_mul (x, key->h);
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
383
384
385
    }
  if (length > 0)
    {
386
387
      memxor (x->b, data, length);
      gcm_gf_mul (x, key->h);
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
388
389
390
391
    }
}

void
392
gcm_auth(struct gcm_ctx *ctx, const struct gcm_key *key,
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
393
394
395
396
397
	 unsigned length, const uint8_t *data)
{
  assert(ctx->auth_size % GCM_BLOCK_SIZE == 0);
  assert(ctx->data_size % GCM_BLOCK_SIZE == 0);

398
  gcm_hash(key, &ctx->x, length, data);
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
399
400
401
402
403
404

  ctx->auth_size += length;
}

static void
gcm_crypt(struct gcm_ctx *ctx, void *cipher, nettle_crypt_func *f,
405
	  unsigned length, uint8_t *dst, const uint8_t *src)
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
406
407
408
409
410
411
412
413
414
{
  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))
        {
415
          f (cipher, GCM_BLOCK_SIZE, dst, ctx->ctr.b);
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
416
417
418
419
420
421
422
423
424
425
          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))
        {
426
          f (cipher, GCM_BLOCK_SIZE, buffer, ctx->ctr.b);
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
427
428
429
430
431
432
433
          memxor3 (dst, src, buffer, GCM_BLOCK_SIZE);
          INC32 (ctx->ctr);
        }
    }
  if (length > 0)
    {
      /* A final partial block */
434
      f (cipher, GCM_BLOCK_SIZE, buffer, ctx->ctr.b);
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
435
436
437
438
439
440
      memxor3 (dst, src, buffer, length);
      INC32 (ctx->ctr);
    }
}

void
441
442
443
gcm_encrypt (struct gcm_ctx *ctx, const struct gcm_key *key,
	     void *cipher, nettle_crypt_func *f,
	     unsigned length, uint8_t *dst, const uint8_t *src)
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
444
445
446
447
{
  assert(ctx->data_size % GCM_BLOCK_SIZE == 0);

  gcm_crypt(ctx, cipher, f, length, dst, src);
448
  gcm_hash(key, &ctx->x, length, dst);
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
449
450
451
452
453

  ctx->data_size += length;
}

void
454
455
gcm_decrypt(struct gcm_ctx *ctx, const struct gcm_key *key,
	    void *cipher, nettle_crypt_func *f,
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
456
457
458
459
	    unsigned length, uint8_t *dst, const uint8_t *src)
{
  assert(ctx->data_size % GCM_BLOCK_SIZE == 0);

460
  gcm_hash(key, &ctx->x, length, src);
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
461
462
463
464
465
466
  gcm_crypt(ctx, cipher, f, length, dst, src);

  ctx->data_size += length;
}

void
467
468
gcm_digest(struct gcm_ctx *ctx, const struct gcm_key *key,
	   void *cipher, nettle_crypt_func *f,
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
469
470
471
472
473
474
475
476
477
478
479
480
	   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);

481
  gcm_hash(key, &ctx->x, GCM_BLOCK_SIZE, buffer);
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
482

483
484
  f (cipher, GCM_BLOCK_SIZE, buffer, ctx->iv.b);
  memxor3 (digest, ctx->x.b, buffer, length);
Nikos Mavrogiannopoulos's avatar
Nikos Mavrogiannopoulos committed
485
486
487

  return;
}