Coverage Report

Created: 2026-10-17 07:19

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gnupg/g10/cipher-aead.c
Line
Count
Source
1
/* cipher-aead.c - Enciphering filter for AEAD modes
2
 * Copyright (C) 2018 Werner koch
3
 *
4
 * This file is part of GnuPG.
5
 *
6
 * GnuPG is free software; you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation; either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * GnuPG is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, see <https://www.gnu.org/licenses/>.
18
 * SPDX-License-Identifier: GPL-3.0+
19
 */
20
21
#include <config.h>
22
#include <stdio.h>
23
#include <stdlib.h>
24
#include <string.h>
25
#include <errno.h>
26
27
#include "gpg.h"
28
#include "../common/status.h"
29
#include "../common/iobuf.h"
30
#include "../common/util.h"
31
#include "filter.h"
32
#include "packet.h"
33
#include "options.h"
34
#include "main.h"
35
36
37
/* The size of the buffer we allocate to encrypt the data.  This must
38
 * be a multiple of the OCB blocksize (16 byte).  */
39
0
#define AEAD_ENC_BUFFER_SIZE (64*1024)
40
41
42
/* Wrapper around iobuf_write to make sure that a proper error code is
43
 * always returned.  */
44
static gpg_error_t
45
my_iobuf_write (iobuf_t a, const void *buffer, size_t buflen)
46
0
{
47
0
  if (iobuf_write (a, buffer, buflen))
48
0
    {
49
0
      gpg_error_t err = iobuf_error (a);
50
0
      if (!err || !gpg_err_code (err)) /* (The latter should never happen) */
51
0
        err = gpg_error (GPG_ERR_EIO);
52
0
      return err;
53
0
    }
54
0
  return 0;
55
0
}
56
57
58
/* Set the nonce and the additional data for the current chunk.  If
59
 * FINAL is set the final AEAD chunk is processed.  This also reset
60
 * the encryption machinery so that the handle can be used for a new
61
 * chunk.  */
62
static gpg_error_t
63
set_nonce_and_ad (cipher_filter_context_t *cfx, int final)
64
0
{
65
0
  gpg_error_t err;
66
0
  unsigned char nonce[16];
67
0
  unsigned char ad[21];
68
0
  int adlen;
69
0
  int i;
70
71
0
  switch (cfx->dek->use_aead)
72
0
    {
73
0
    case AEAD_ALGO_OCB:
74
0
      memcpy (nonce, cfx->startiv, 15);
75
0
      i = 7;
76
0
      break;
77
78
0
    case AEAD_ALGO_EAX:
79
0
      memcpy (nonce, cfx->startiv, 16);
80
0
      i = 8;
81
0
      break;
82
83
0
    case AEAD_ALGO_GCM:
84
0
      memcpy (nonce, cfx->startiv, 12);
85
0
      i = 4;
86
0
      break;
87
88
0
    default:
89
0
      BUG ();
90
0
    }
91
92
0
  nonce[i++] ^= cfx->chunkindex >> 56;
93
0
  nonce[i++] ^= cfx->chunkindex >> 48;
94
0
  nonce[i++] ^= cfx->chunkindex >> 40;
95
0
  nonce[i++] ^= cfx->chunkindex >> 32;
96
0
  nonce[i++] ^= cfx->chunkindex >> 24;
97
0
  nonce[i++] ^= cfx->chunkindex >> 16;
98
0
  nonce[i++] ^= cfx->chunkindex >>  8;
99
0
  nonce[i++] ^= cfx->chunkindex;
100
101
0
  if (DBG_CRYPTO)
102
0
    log_printhex (nonce, i, "nonce:");
103
0
  err = gcry_cipher_setiv (cfx->cipher_hd, nonce, i);
104
0
  if (err)
105
0
    return err;
106
107
0
  ad[0] = (0xc0 | (cfx->seipdv2? PKT_ENCRYPTED_MDC : PKT_ENCRYPTED_OCB));
108
0
  ad[1] = cfx->seipdv2? 2 : 1;
109
0
  ad[2] = cfx->dek->algo;
110
0
  ad[3] = cfx->dek->use_aead;
111
0
  ad[4] = cfx->chunkbyte;
112
0
  ad[5] = cfx->chunkindex >> 56;
113
0
  ad[6] = cfx->chunkindex >> 48;
114
0
  ad[7] = cfx->chunkindex >> 40;
115
0
  ad[8] = cfx->chunkindex >> 32;
116
0
  ad[9] = cfx->chunkindex >> 24;
117
0
  ad[10]= cfx->chunkindex >> 16;
118
0
  ad[11]= cfx->chunkindex >>  8;
119
0
  ad[12]= cfx->chunkindex;
120
0
  if (final)
121
0
    {
122
0
      ad[13] = cfx->total >> 56;
123
0
      ad[14] = cfx->total >> 48;
124
0
      ad[15] = cfx->total >> 40;
125
0
      ad[16] = cfx->total >> 32;
126
0
      ad[17] = cfx->total >> 24;
127
0
      ad[18] = cfx->total >> 16;
128
0
      ad[19] = cfx->total >>  8;
129
0
      ad[20] = cfx->total;
130
0
    }
131
132
0
  if (cfx->seipdv2)
133
0
    {
134
0
      adlen = 5;
135
0
      if (final)
136
0
        {
137
0
          adlen += 8;
138
0
          memmove (ad+5, ad+13, 8);
139
0
        }
140
0
    }
141
0
  else
142
0
    adlen = final? 21 : 13;
143
144
0
  if (DBG_CRYPTO)
145
0
    log_printhex (ad, adlen, "authdata:");
146
0
  return gcry_cipher_authenticate (cfx->cipher_hd, ad, adlen);
147
0
}
148
149
150
static gpg_error_t
151
write_header (cipher_filter_context_t *cfx, iobuf_t a)
152
0
{
153
0
  gpg_error_t err;
154
0
  PACKET pkt;
155
0
  PKT_encrypted ed;
156
0
  unsigned int blocksize;
157
0
  unsigned int startivlen;
158
0
  enum gcry_cipher_modes ciphermode;
159
0
  unsigned char *thekeybuffer = NULL; /* Used by seipdv2  */
160
0
  size_t thekeybufferlen;             /* Malloced length of thekeybuffer.  */
161
162
0
  log_assert (cfx->dek->use_aead);
163
164
0
  blocksize = openpgp_cipher_get_algo_blklen (cfx->dek->algo);
165
0
  if (blocksize != 16 )
166
0
    log_fatal ("unsupported blocksize %u for AEAD\n", blocksize);
167
168
0
  err = openpgp_aead_algo_info (cfx->dek->use_aead, &ciphermode, &startivlen);
169
0
  if (err)
170
0
    goto leave;
171
172
0
  log_assert (opt.chunk_size >= 6 && opt.chunk_size <= 62);
173
0
  cfx->chunkbyte = opt.chunk_size - 6;
174
0
  cfx->chunksize = (uint64_t)1 << (cfx->chunkbyte + 6);
175
0
  cfx->chunklen = 0;
176
0
  cfx->bufsize = AEAD_ENC_BUFFER_SIZE;
177
0
  cfx->buflen = 0;
178
0
  cfx->buffer = xtrymalloc (cfx->bufsize);
179
0
  if (!cfx->buffer)
180
0
    return gpg_error_from_syserror ();
181
182
0
  init_packet (&pkt);
183
0
  memset (&ed, 0, sizeof ed);
184
0
  ed.new_ctb = 1;  /* (Is anyway required for the packet type).  */
185
0
  ed.len = 0; /* fixme: cfx->datalen */
186
0
  ed.cipher_algo = cfx->dek->algo;
187
0
  ed.aead_algo   = cfx->dek->use_aead;
188
0
  ed.chunkbyte   = cfx->chunkbyte;
189
0
  ed.extralen    = startivlen + 16; /* (16 is the taglen) */
190
0
  if (cfx->seipdv2)
191
0
    {
192
0
      pkt.pkttype = PKT_ENCRYPTED_MDC;
193
0
      ed.version = 2;
194
0
    }
195
0
  else
196
0
    {
197
0
      pkt.pkttype = PKT_ENCRYPTED_OCB;
198
0
      ed.version = 1;
199
0
    }
200
0
  pkt.pkt.encrypted = &ed;
201
202
0
  if (DBG_FILTER)
203
0
    log_debug ("aead packet: len=%lu extralen=%d version=%d\n",
204
0
               (unsigned long)ed.len, ed.extralen, ed.version);
205
206
0
  print_cipher_algo_note (cfx->dek->algo);
207
208
0
  if (build_packet( a, &pkt))
209
0
    log_bug ("build_packet(%s) failed\n",
210
0
             cfx->seipdv2? "ENCRYPTED_MDCv2":"ENCRYPTED_OCB");
211
212
0
  log_assert (startivlen <= sizeof cfx->startiv && startivlen >= 8);
213
0
  if (cfx->seipdv2)
214
0
    {
215
0
      gcry_kdf_hd_t kdfhd;
216
0
      unsigned long kdfparam[1];
217
0
      unsigned char kdfinfo[5];
218
0
      unsigned char salt[32];
219
220
      /* Expand key to M + N - 8.  */
221
0
      log_assert (cfx->dek->keylen >= 16);
222
0
      kdfparam[0] = cfx->dek->keylen + startivlen - 8;
223
224
0
      kdfinfo[0] = 0xd2;
225
0
      kdfinfo[1] = 2;
226
0
      kdfinfo[2] = ed.cipher_algo;
227
0
      kdfinfo[3] = ed.aead_algo;
228
0
      kdfinfo[4] = ed.chunkbyte;
229
230
0
      gcry_randomize (salt, sizeof salt, GCRY_STRONG_RANDOM);
231
0
      err = my_iobuf_write (a, salt, sizeof salt);
232
0
      if (err)
233
0
        goto leave;
234
235
0
      if (DBG_CRYPTO)
236
0
        {
237
0
          log_printhex (salt, sizeof salt, "salt:");
238
0
          log_printhex (kdfinfo, sizeof kdfinfo, "info:");
239
0
        }
240
0
      err = gcry_kdf_open (&kdfhd, GCRY_KDF_HKDF, GCRY_MAC_HMAC_SHA256,
241
0
                           kdfparam, 1,
242
0
                           cfx->dek->key, cfx->dek->keylen,
243
0
                           NULL, 0,
244
0
                           salt, sizeof salt,
245
0
                           kdfinfo, sizeof kdfinfo);
246
0
      if (!err)
247
0
        {
248
0
          err = gcry_kdf_compute (kdfhd, NULL);
249
0
          if (!err)
250
0
            {
251
              /* Allocate M + N */
252
0
              thekeybufferlen = cfx->dek->keylen + startivlen;
253
0
              thekeybuffer = xtrymalloc_secure (thekeybufferlen);
254
0
              if (!thekeybuffer)
255
0
                err = gpg_error_from_syserror ();
256
0
              else /* Get M + N - 8 */
257
0
                err = gcry_kdf_final (kdfhd, kdfparam[0], thekeybuffer);
258
0
            }
259
0
          gcry_kdf_close (kdfhd);
260
0
        }
261
0
      if (err)
262
0
        {
263
0
          log_error ("HKDF for session key failed: %s\n", gpg_strerror (err));
264
0
          goto leave;
265
0
        }
266
0
      memcpy (cfx->startiv, thekeybuffer + cfx->dek->keylen, startivlen - 8);
267
0
      memset (cfx->startiv + startivlen - 8, 0, 8);
268
269
0
      err = openpgp_cipher_open (&cfx->cipher_hd, cfx->dek->algo,
270
0
                                 ciphermode, GCRY_CIPHER_SECURE);
271
0
      if (err)
272
0
        goto leave;
273
274
0
      if (DBG_CRYPTO)
275
0
        log_printhex (thekeybuffer, cfx->dek->keylen, "thekey:");
276
0
      err = gcry_cipher_setkey (cfx->cipher_hd,
277
0
                                thekeybuffer, cfx->dek->keylen);
278
0
    }
279
0
  else
280
0
    {
281
0
      log_assert (sizeof cfx->startiv >= startivlen);
282
0
      gcry_randomize (cfx->startiv, startivlen, GCRY_STRONG_RANDOM);
283
0
      err = my_iobuf_write (a, cfx->startiv, startivlen);
284
0
      if (err)
285
0
        goto leave;
286
287
0
      err = openpgp_cipher_open (&cfx->cipher_hd, cfx->dek->algo,
288
0
                                 ciphermode, GCRY_CIPHER_SECURE);
289
0
      if (err)
290
0
        goto leave;
291
292
0
      if (DBG_CRYPTO)
293
0
        log_printhex (cfx->dek->key, cfx->dek->keylen, "thekey:");
294
0
      err = gcry_cipher_setkey (cfx->cipher_hd,
295
0
                                cfx->dek->key, cfx->dek->keylen);
296
0
    }
297
0
  if (err)
298
0
    goto leave;
299
300
0
  cfx->wrote_header = 1;
301
302
0
 leave:
303
0
  if (thekeybuffer)
304
0
    {
305
0
      wipememory (thekeybuffer, thekeybufferlen);
306
0
      xfree (thekeybuffer);
307
0
    }
308
0
  return err;
309
0
}
310
311
312
/* Get and write the auth tag to stream A.  */
313
static gpg_error_t
314
write_auth_tag (cipher_filter_context_t *cfx, iobuf_t a)
315
0
{
316
0
  gpg_error_t err;
317
0
  char tag[16];
318
319
0
  err = gcry_cipher_gettag (cfx->cipher_hd, tag, 16);
320
0
  if (err)
321
0
    goto leave;
322
0
  err = my_iobuf_write (a, tag, 16);
323
0
  if (err)
324
0
    goto leave;
325
326
0
 leave:
327
0
  if (err)
328
0
    log_error ("write_auth_tag failed: %s\n", gpg_strerror (err));
329
0
  return err;
330
0
}
331
332
333
/* Write the final chunk to stream A.  */
334
static gpg_error_t
335
write_final_chunk (cipher_filter_context_t *cfx, iobuf_t a)
336
0
{
337
0
  gpg_error_t err;
338
0
  char dummy[1];
339
340
0
  err = set_nonce_and_ad (cfx, 1);
341
0
  if (err)
342
0
    goto leave;
343
344
0
  gcry_cipher_final (cfx->cipher_hd);
345
346
  /* Encrypt an empty string.  */
347
0
  err = gcry_cipher_encrypt (cfx->cipher_hd, dummy, 0, NULL, 0);
348
0
  if (err)
349
0
    goto leave;
350
351
0
  err = write_auth_tag (cfx, a);
352
353
0
 leave:
354
0
  return err;
355
0
}
356
357
358
/* The core of the flush sub-function of cipher_filter_aead.   */
359
static gpg_error_t
360
do_flush (cipher_filter_context_t *cfx, iobuf_t a, byte *buf, size_t size)
361
0
{
362
0
  gpg_error_t err = 0;
363
0
  int finalize = 0;
364
0
  size_t n;
365
366
  /* Put the data into a buffer, flush and encrypt as needed.  */
367
0
  if (DBG_FILTER)
368
0
    log_debug ("flushing %zu bytes (cur buflen=%zu)\n", size, cfx->buflen);
369
0
  do
370
0
    {
371
0
      const unsigned fast_threshold = 512;
372
0
      const byte *src_buf = NULL;
373
0
      int enc_now = 0;
374
375
0
      if (cfx->buflen + size < cfx->bufsize)
376
0
        n = size;
377
0
      else
378
0
        n = cfx->bufsize - cfx->buflen;
379
380
0
      if (cfx->buflen % fast_threshold != 0)
381
0
  {
382
    /* Attempt to align cfx->buflen to fast threshold size first. */
383
0
    size_t nalign = fast_threshold - (cfx->buflen % fast_threshold);
384
0
    if (nalign < n)
385
0
      {
386
0
        n = nalign;
387
0
      }
388
0
  }
389
0
      else if (cfx->buflen == 0 && n >= fast_threshold)
390
0
  {
391
    /* Handle large input buffers as multiple of cipher blocksize. */
392
0
    n = (n / 16) * 16;
393
0
  }
394
395
0
      if (cfx->chunklen + cfx->buflen + n >= cfx->chunksize)
396
0
        {
397
0
          size_t n1 = cfx->chunksize - (cfx->chunklen + cfx->buflen);
398
0
          finalize = 1;
399
0
          if (DBG_FILTER)
400
0
            log_debug ("chunksize %llu reached;"
401
0
                       " cur buflen=%zu using %zu of %zu\n",
402
0
                       (unsigned long long)cfx->chunksize, cfx->buflen,
403
0
                       n1, n);
404
0
          n = n1;
405
0
        }
406
407
0
      if (!finalize && cfx->buflen % 16 == 0 && cfx->buflen > 0
408
0
    && size >= fast_threshold)
409
0
  {
410
    /* If cfx->buffer is aligned and remaining input buffer length
411
     * is long, encrypt cfx->buffer inplace now to allow fast path
412
     * handling on next loop iteration. */
413
0
    src_buf = cfx->buffer;
414
0
    enc_now = 1;
415
0
    n = 0;
416
0
  }
417
0
      else if (cfx->buflen == 0 && n >= fast_threshold)
418
0
  {
419
    /* Fast path for large input buffer. This avoids memcpy and
420
     * instead encrypts directly from input to cfx->buffer. */
421
0
    log_assert (n % 16 == 0 || finalize);
422
0
    src_buf = buf;
423
0
    cfx->buflen = n;
424
0
    buf += n;
425
0
    size -= n;
426
0
    enc_now = 1;
427
0
  }
428
0
      else if (n > 0)
429
0
  {
430
0
    memcpy (cfx->buffer + cfx->buflen, buf, n);
431
0
    src_buf = cfx->buffer;
432
0
    cfx->buflen += n;
433
0
    buf  += n;
434
0
    size -= n;
435
0
  }
436
437
0
      if (cfx->buflen == cfx->bufsize || enc_now || finalize)
438
0
        {
439
0
          if (DBG_FILTER)
440
0
            log_debug ("encrypting: size=%zu buflen=%zu %s%s n=%zu\n",
441
0
                       size, cfx->buflen, finalize?"(finalize)":"",
442
0
           enc_now?"(now)":"", n);
443
444
0
          if (!cfx->chunklen)
445
0
            {
446
0
              if (DBG_FILTER)
447
0
                log_debug ("start encrypting a new chunk\n");
448
0
              err = set_nonce_and_ad (cfx, 0);
449
0
              if (err)
450
0
                goto leave;
451
0
            }
452
453
0
          if (finalize)
454
0
            gcry_cipher_final (cfx->cipher_hd);
455
0
          if (DBG_FILTER)
456
0
            {
457
0
              if (finalize)
458
0
                log_printhex (src_buf, cfx->buflen, "plain(1):");
459
0
              else if (cfx->buflen > 32)
460
0
                log_printhex (src_buf + cfx->buflen - 32, 32,
461
0
                              "plain(last32):");
462
0
            }
463
464
          /* Take care: even with a buflen of zero an encrypt needs to
465
           * be called after gcry_cipher_final and before
466
           * gcry_cipher_gettag - at least with libgcrypt 1.8 and OCB
467
           * mode.  */
468
0
    err = gcry_cipher_encrypt (cfx->cipher_hd, cfx->buffer,
469
0
             cfx->buflen, src_buf, cfx->buflen);
470
0
          if (err)
471
0
            goto leave;
472
0
          if (finalize && DBG_FILTER)
473
0
            log_printhex (cfx->buffer, cfx->buflen, "ciphr(1):");
474
0
          err = my_iobuf_write (a, cfx->buffer, cfx->buflen);
475
0
          if (err)
476
0
            goto leave;
477
0
          cfx->chunklen += cfx->buflen;
478
0
          cfx->total += cfx->buflen;
479
0
          cfx->buflen = 0;
480
481
0
          if (finalize)
482
0
            {
483
0
              if (DBG_FILTER)
484
0
                log_debug ("writing tag: chunklen=%ju total=%ju\n",
485
0
                           (uintmax_t)cfx->chunklen, (uintmax_t)cfx->total);
486
0
              err = write_auth_tag (cfx, a);
487
0
              if (err)
488
0
                goto leave;
489
490
0
              cfx->chunkindex++;
491
0
              cfx->chunklen = 0;
492
0
              finalize = 0;
493
0
            }
494
0
        }
495
0
    }
496
0
  while (size);
497
498
0
 leave:
499
0
  return err;
500
0
}
501
502
503
/* The core of the free sub-function of cipher_filter_aead.   */
504
static gpg_error_t
505
do_free (cipher_filter_context_t *cfx, iobuf_t a)
506
0
{
507
0
  gpg_error_t err = 0;
508
509
0
  if (DBG_FILTER)
510
0
    log_debug ("do_free: buflen=%zu\n", cfx->buflen);
511
512
0
  if (cfx->chunklen || cfx->buflen)
513
0
    {
514
0
      if (DBG_FILTER)
515
0
        log_debug ("encrypting last %zu bytes of the last chunk\n",cfx->buflen);
516
517
0
      if (!cfx->chunklen)
518
0
        {
519
0
          if (DBG_FILTER)
520
0
            log_debug ("start encrypting a new chunk\n");
521
0
          err = set_nonce_and_ad (cfx, 0);
522
0
          if (err)
523
0
            goto leave;
524
0
        }
525
526
0
      gcry_cipher_final (cfx->cipher_hd);
527
0
      err = gcry_cipher_encrypt (cfx->cipher_hd, cfx->buffer, cfx->buflen,
528
0
                                 NULL, 0);
529
0
      if (err)
530
0
        goto leave;
531
0
      err = my_iobuf_write (a, cfx->buffer, cfx->buflen);
532
0
      if (err)
533
0
        goto leave;
534
      /* log_printhex (cfx->buffer, cfx->buflen, "wrote:"); */
535
0
      cfx->chunklen += cfx->buflen;
536
0
      cfx->total += cfx->buflen;
537
538
      /* Get and write the authentication tag.  */
539
0
      if (DBG_FILTER)
540
0
        log_debug ("writing tag: chunklen=%ju total=%ju\n",
541
0
                   (uintmax_t)cfx->chunklen, (uintmax_t)cfx->total);
542
0
      err = write_auth_tag (cfx, a);
543
0
      if (err)
544
0
        goto leave;
545
0
      cfx->chunkindex++;
546
0
      cfx->chunklen = 0;
547
0
    }
548
549
  /* Write the final chunk.  */
550
0
  if (DBG_FILTER)
551
0
    log_debug ("creating final chunk\n");
552
0
  err = write_final_chunk (cfx, a);
553
554
0
 leave:
555
0
  xfree (cfx->buffer);
556
0
  cfx->buffer = NULL;
557
0
  gcry_cipher_close (cfx->cipher_hd);
558
0
  cfx->cipher_hd = NULL;
559
0
  return err;
560
0
}
561
562
563
/*
564
 * This filter is used to encrypt data with an AEAD algorithm
565
 */
566
int
567
cipher_filter_aead (void *opaque, int control,
568
                    iobuf_t a, byte *buf, size_t *ret_len)
569
0
{
570
0
  cipher_filter_context_t *cfx = opaque;
571
0
  size_t size = *ret_len;
572
0
  int rc = 0;
573
574
0
  if (control == IOBUFCTRL_UNDERFLOW) /* decrypt */
575
0
    {
576
0
      rc = -1; /* not used */
577
0
    }
578
0
  else if (control == IOBUFCTRL_FLUSH) /* encrypt */
579
0
    {
580
0
      if (!cfx->wrote_header && (rc=write_header (cfx, a)))
581
0
        ;
582
0
      else
583
0
        rc = do_flush (cfx, a, buf, size);
584
0
    }
585
0
  else if (control == IOBUFCTRL_FREE)
586
0
    {
587
0
      rc = do_free (cfx, a);
588
0
    }
589
0
  else if (control == IOBUFCTRL_DESC)
590
0
    {
591
0
      mem2str (buf, "cipher_filter_aead", *ret_len);
592
0
    }
593
0
  else if (control == IOBUFCTRL_INIT)
594
0
    {
595
0
      write_status_printf (STATUS_BEGIN_ENCRYPTION, "0 %d %d",
596
0
                           cfx->dek->algo, cfx->dek->use_aead);
597
0
    }
598
599
0
  return rc;
600
0
}