Coverage Report

Created: 2026-01-09 06:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gnupg/common/kem.c
Line
Count
Source
1
/* kem.c -  KEM helper functions
2
 * Copyright (C) 2024  g10 Code GmbH.
3
 *
4
 * This file is part of GnuPG.
5
 *
6
 * GnuPG is free software; you can redistribute and/or modify this
7
 * part of GnuPG under the terms of either
8
 *
9
 *   - the GNU Lesser General Public License as published by the Free
10
 *     Software Foundation; either version 3 of the License, or (at
11
 *     your option) any later version.
12
 *
13
 * or
14
 *
15
 *   - the GNU General Public License as published by the Free
16
 *     Software Foundation; either version 2 of the License, or (at
17
 *     your option) any later version.
18
 *
19
 * or both in parallel, as here.
20
 *
21
 * GnuPG is distributed in the hope that it will be useful, but
22
 * WITHOUT ANY WARRANTY; without even the implied warranty of
23
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
24
 * General Public License for more details.
25
 *
26
 * You should have received copies of the GNU General Public License
27
 * and the GNU Lesser General Public License along with this program;
28
 * if not, see <https://www.gnu.org/licenses/>.
29
 * SPDX-License-Identifier: (LGPL-3.0-or-later OR GPL-2.0-or-later)
30
 */
31
32
#include <config.h>
33
#include <stdlib.h>
34
#include <string.h>
35
#include <gpg-error.h>
36
#include <gcrypt.h>
37
#include "mischelp.h"
38
#include "util.h"
39
40
/* domSeperation as per *PGP specs. */
41
0
#define KMAC_KEY "OpenPGPCompositeKeyDerivationFunction"
42
43
/* customizationString as per *PGP specs. */
44
0
#define KMAC_CUSTOM "KDF"
45
46
/* The blocksize used for Keccak by compute_kmac256.  */
47
0
#define KECCAK512_BLOCKSIZE 136
48
49
50
51
static gpg_error_t
52
compute_kmac256 (void *digest, size_t digestlen,
53
                 const void *key, size_t keylen,
54
                 const void *custom, size_t customlen,
55
                 gcry_buffer_t *data_iov, int data_iovlen)
56
0
{
57
0
  gpg_error_t err;
58
0
  gcry_buffer_t iov[20];
59
0
  const unsigned char headPAD[2] = { 1, KECCAK512_BLOCKSIZE };
60
0
  unsigned char headK[3];
61
0
  const unsigned char pad[KECCAK512_BLOCKSIZE] = { 0 };
62
0
  unsigned char right_encode_L[3];
63
0
  unsigned int len;
64
0
  int iovcnt;
65
66
0
  if (data_iovlen >= DIM(iov) - 6)
67
0
    return gpg_error (GPG_ERR_TOO_LARGE);
68
69
  /* Check the validity conditions of NIST SP 800-185 */
70
0
  if (keylen >= 255 || customlen >= 255 || digestlen >= 255)
71
0
    return gpg_error (GPG_ERR_TOO_LARGE);
72
73
0
  iovcnt = 0;
74
0
  iov[iovcnt].data = "KMAC";
75
0
  iov[iovcnt].off = 0;
76
0
  iov[iovcnt].len = 4;
77
0
  iovcnt++;
78
79
0
  iov[iovcnt].data = (void *)custom;
80
0
  iov[iovcnt].off = 0;
81
0
  iov[iovcnt].len = customlen;
82
0
  iovcnt++;
83
84
0
  iov[iovcnt].data = (void *)headPAD;
85
0
  iov[iovcnt].off = 0;
86
0
  iov[iovcnt].len = sizeof (headPAD);
87
0
  iovcnt++;
88
89
0
  if (keylen < 32)
90
0
    {
91
0
      headK[0] = 1;
92
0
      headK[1] = (keylen*8)&0xff;
93
0
      iov[iovcnt].data = headK;
94
0
      iov[iovcnt].off = 0;
95
0
      iov[iovcnt].len = 2;
96
0
    }
97
0
  else
98
0
    {
99
0
      headK[0] = 2;
100
0
      headK[1] = (keylen*8)>>8;
101
0
      headK[2] = (keylen*8)&0xff;
102
0
      iov[iovcnt].data = headK;
103
0
      iov[iovcnt].off = 0;
104
0
      iov[iovcnt].len = 3;
105
0
    }
106
0
  iovcnt++;
107
108
0
  iov[iovcnt].data = (void *)key;
109
0
  iov[iovcnt].off = 0;
110
0
  iov[iovcnt].len = keylen;
111
0
  iovcnt++;
112
113
0
  len = iov[2].len + iov[3].len + iov[4].len;
114
0
  len %= KECCAK512_BLOCKSIZE;
115
116
0
  iov[iovcnt].data = (unsigned char *)pad;
117
0
  iov[iovcnt].off = 0;
118
0
  iov[iovcnt].len = sizeof (pad) - len;
119
0
  iovcnt++;
120
121
0
  memcpy (&iov[iovcnt], data_iov, data_iovlen * sizeof (gcry_buffer_t));
122
0
  iovcnt += data_iovlen;
123
124
0
  if (digestlen < 32)
125
0
    {
126
0
      right_encode_L[0] = (digestlen * 8) & 0xff;
127
0
      right_encode_L[1] = 1;
128
0
    }
129
0
  else
130
0
    {
131
0
      right_encode_L[0] = (digestlen * 8) >> 8;
132
0
      right_encode_L[1] = (digestlen * 8) & 0xff;
133
0
      right_encode_L[2] = 2;
134
0
    }
135
136
0
  iov[iovcnt].data = right_encode_L;
137
0
  iov[iovcnt].off = 0;
138
0
  iov[iovcnt].len = 3;
139
0
  iovcnt++;
140
141
0
  err = gcry_md_hash_buffers_ext (GCRY_MD_CSHAKE256, 0,
142
0
                                  digest, digestlen, iov, iovcnt);
143
0
  return err;
144
0
}
145
146
147
/* Compute KEK for ECC with HASHALGO, ECDH result, and KDF_PARAMS.
148
 *
149
 * KDF_PARAMS is specified by upper layer.
150
 */
151
gpg_error_t
152
gnupg_ecc_kem_kdf (void *kek, size_t kek_len, int is_pgp,
153
                   int hashalgo, const void *ecdh, size_t ecdh_len,
154
                   const unsigned char *kdf_params, size_t kdf_params_len)
155
0
{
156
  /* Traditional ECC */
157
0
  gpg_error_t err;
158
0
  gcry_kdf_hd_t hd;
159
0
  unsigned long param[1];
160
0
  int kdf_algo;
161
162
0
  if (is_pgp)
163
0
    kdf_algo = GCRY_KDF_ONESTEP_KDF;
164
0
  else
165
0
    kdf_algo = GCRY_KDF_X963_KDF;
166
167
0
  param[0] = kek_len;
168
0
  err = gcry_kdf_open (&hd, kdf_algo, hashalgo, param, 1,
169
0
                       ecdh, ecdh_len, NULL, 0, NULL, 0,
170
0
                       kdf_params, kdf_params_len);
171
0
  if (!err)
172
0
    {
173
0
      gcry_kdf_compute (hd, NULL);
174
0
      gcry_kdf_final (hd, kek_len, kek);
175
0
      gcry_kdf_close (hd);
176
0
    }
177
178
0
  return err;
179
0
}
180
181
182
/* Compute KEK for ECC with HASHALGO, ECDH result, ciphertext in
183
 * ECC_CT (which is an ephemeral key), and public key in ECC_PK.
184
 */
185
gpg_error_t
186
gnupg_ecc_kem_simple_kdf (void *kek, size_t kek_len,
187
                          int hashalgo, const void *ecdh, size_t ecdh_len,
188
                          const void *ecc_ct, size_t ecc_ct_len,
189
                          const void *ecc_pk, size_t ecc_pk_len)
190
0
{
191
  /* ECC part in composite KEM */
192
0
  gcry_buffer_t iov[3];
193
0
  unsigned int dlen;
194
195
0
  dlen = gcry_md_get_algo_dlen (hashalgo);
196
0
  if (kek_len != dlen)
197
0
    return gpg_error (GPG_ERR_INV_LENGTH);
198
199
0
  memset (iov, 0, sizeof (iov));
200
201
0
  iov[0].data = (unsigned char *)ecdh;
202
0
  iov[0].len = ecdh_len;
203
0
  iov[1].data = (unsigned char *)ecc_ct;
204
0
  iov[1].len = ecc_ct_len;
205
0
  iov[2].data = (unsigned char *)ecc_pk;
206
0
  iov[2].len = ecc_pk_len;
207
0
  gcry_md_hash_buffers (hashalgo, 0, kek, iov, 3);
208
209
0
  return 0;
210
0
}
211
212
/* Compute KEK by combining two KEMs.  The caller provides a buffer
213
 * KEK allocated with size KEK_LEN which will receive the computed
214
 * KEK. (ECC_SS, ECC_SS_LEN) is the shared secret of the first key.
215
 * (ECC_CT, ECC_CT_LEN) is the ciphertext of the first key.
216
 * (MLKEM_SS, ECC_SS_LEN) is the shared secret of the second key.
217
 * (MLKEM_CT, MLKEM_CT_LEN) is the ciphertext of the second key.
218
 * (FIXEDINFO, FIXEDINFO_LEN) is an octet string used to bind the KEK
219
 * to a the key; for PGP we use the concatenation of the session key's
220
 * algorithm id and the v5 fingerprint of the key.
221
 */
222
gpg_error_t
223
gnupg_kem_combiner (void *kek, size_t kek_len,
224
                    const void *ecc_ss, size_t ecc_ss_len,
225
                    const void *ecc_ct, size_t ecc_ct_len,
226
                    const void *mlkem_ss, size_t mlkem_ss_len,
227
                    const void *mlkem_ct, size_t mlkem_ct_len,
228
                    const void *fixedinfo, size_t fixedinfo_len)
229
0
{
230
0
  gpg_error_t err;
231
0
  gcry_buffer_t iov[6];
232
233
0
  memset (iov, 0, sizeof (iov));
234
235
0
  iov[0].data = "\x00\x00\x00\x01"; /* Counter */
236
0
  iov[0].len = 4;
237
238
0
  iov[1].data = (unsigned char *)ecc_ss;
239
0
  iov[1].len = ecc_ss_len;
240
241
0
  iov[2].data = (unsigned char *)ecc_ct;
242
0
  iov[2].len = ecc_ct_len;
243
244
0
  iov[3].data = (unsigned char *)mlkem_ss;
245
0
  iov[3].len = mlkem_ss_len;
246
247
0
  iov[4].data = (unsigned char *)mlkem_ct;
248
0
  iov[4].len = mlkem_ct_len;
249
250
0
  iov[5].data = (unsigned char *)fixedinfo;
251
0
  iov[5].len = fixedinfo_len;
252
253
0
  err = compute_kmac256 (kek, kek_len,
254
0
                         KMAC_KEY, strlen (KMAC_KEY),
255
0
                         KMAC_CUSTOM, strlen (KMAC_CUSTOM), iov, 6);
256
0
  return err;
257
0
}
258

259
#define ECC_CURVE25519_INDEX 0
260
static const struct gnupg_ecc_params ecc_table[] =
261
  {
262
    {
263
      "Curve25519",
264
      33, 32, 32,
265
      GCRY_MD_SHA3_256, GCRY_KEM_RAW_X25519,
266
      1, 1, 0
267
    },
268
    {
269
      "X448",
270
      56, 56, 56,
271
      GCRY_MD_SHA3_512, GCRY_KEM_RAW_X448,
272
      0, 0, 0
273
    },
274
    {
275
      "NIST P-256",
276
      65, 32, 65,
277
      GCRY_MD_SHA3_256, GCRY_KEM_RAW_P256R1,
278
      0, 0, 1
279
    },
280
    {
281
      "NIST P-384",
282
      97, 48, 97,
283
      GCRY_MD_SHA3_512, GCRY_KEM_RAW_P384R1,
284
      0, 0, 1
285
    },
286
    {
287
      "NIST P-521",
288
      133, 66, 133,
289
      GCRY_MD_SHA3_512, GCRY_KEM_RAW_P521R1,
290
      0, 0, 1
291
    },
292
    {
293
      "brainpoolP256r1",
294
      65, 32, 65,
295
      GCRY_MD_SHA3_256, GCRY_KEM_RAW_BP256,
296
      0, 0, 1
297
    },
298
    {
299
      "brainpoolP384r1",
300
      97, 48, 97,
301
      GCRY_MD_SHA3_512, GCRY_KEM_RAW_BP384,
302
      0, 0, 1
303
    },
304
    {
305
      "brainpoolP512r1",
306
      129, 64, 129,
307
      GCRY_MD_SHA3_512, GCRY_KEM_RAW_BP512,
308
      0, 0, 1
309
    },
310
#ifdef GCRY_KEM_RAW_P256K1
311
    {
312
      "secp256k1",
313
      65, 32, 65,
314
      GCRY_MD_SHA3_256, GCRY_KEM_RAW_P256K1,
315
      0, 0, 1
316
    },
317
#endif
318
    { NULL, 0, 0, 0, 0, 0, 0, 0, 0 }
319
};
320
321
322
/* Return the ECC parameters for CURVE.  CURVE is expected to be the
323
 * canonical name.  */
324
const struct gnupg_ecc_params *
325
gnupg_get_ecc_params (const char *curve)
326
0
{
327
0
  int i;
328
329
0
  for (i = 0; ecc_table[i].curve; i++)
330
0
    if (!strcmp (ecc_table[i].curve, curve))
331
0
      return &ecc_table[i];
332
333
0
  return NULL;
334
0
}