Coverage Report

Created: 2024-11-21 07:03

/src/libgcrypt/cipher/kem-ecc.c
Line
Count
Source (jump to first uncovered line)
1
/* kem-ecc.c - Key Encapsulation Mechanism with ECC
2
 * Copyright (C) 2024 g10 Code GmbH
3
 *
4
 * This file is part of Libgcrypt.
5
 *
6
 * Libgcrypt is free software; you can redistribute it and/or modify
7
 * it under the terms of the GNU Lesser general Public License as
8
 * published by the Free Software Foundation; either version 2.1 of
9
 * the License, or (at your option) any later version.
10
 *
11
 * Libgcrypt 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 Lesser General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General Public
17
 * License along with this program; if not, see <https://www.gnu.org/licenses/>.
18
 * SPDX-License-Identifier: LGPL-2.1-or-later
19
 *
20
 */
21
22
#include <config.h>
23
#include <stdio.h>
24
#include <stdlib.h>
25
#include <string.h>
26
#include <errno.h>
27
28
#include "g10lib.h"
29
#include "cipher.h"
30
31
#include "kem-ecc.h"
32
33
#define ECC_PUBKEY_LEN_MAX 133
34
#define ECC_SECKEY_LEN_MAX 66
35
36
static const char *
37
algo_to_curve (int algo)
38
0
{
39
0
  switch (algo)
40
0
    {
41
0
    case GCRY_KEM_RAW_X25519:
42
0
    case GCRY_KEM_DHKEM25519:
43
0
      return "Curve25519";
44
45
0
    case GCRY_KEM_RAW_X448:
46
0
    case GCRY_KEM_DHKEM448:
47
0
      return "X448";
48
49
0
    case GCRY_KEM_RAW_BP256:
50
0
      return "brainpoolP256r1";
51
52
0
    case GCRY_KEM_RAW_BP384:
53
0
      return "brainpoolP384r1";
54
55
0
    case GCRY_KEM_RAW_BP512:
56
0
      return "brainpoolP512r1";
57
58
0
    case GCRY_KEM_RAW_P256R1:
59
0
      return "NIST P-256";
60
61
0
    case GCRY_KEM_RAW_P384R1:
62
0
      return "NIST P-384";
63
64
0
    case GCRY_KEM_RAW_P521R1:
65
0
      return "NIST P-521";
66
67
0
    default:
68
0
      return 0;
69
0
    }
70
0
}
71
72
73
static int
74
algo_to_seckey_len (int algo)
75
0
{
76
0
  switch (algo)
77
0
    {
78
0
    case GCRY_KEM_RAW_X25519:
79
0
    case GCRY_KEM_DHKEM25519:
80
0
      return 32;
81
82
0
    case GCRY_KEM_RAW_X448:
83
0
    case GCRY_KEM_DHKEM448:
84
0
      return 56;
85
86
0
    case GCRY_KEM_RAW_BP256:
87
0
      return 32;
88
89
0
    case GCRY_KEM_RAW_BP384:
90
0
      return 48;
91
92
0
    case GCRY_KEM_RAW_BP512:
93
0
      return 64;
94
95
0
    case GCRY_KEM_RAW_P256R1:
96
0
      return 32;
97
98
0
    case GCRY_KEM_RAW_P384R1:
99
0
      return 48;
100
101
0
    case GCRY_KEM_RAW_P521R1:
102
0
      return 66;
103
104
0
    default:
105
0
      return 0;
106
0
    }
107
0
}
108
109
110
static gpg_err_code_t
111
ecc_mul_point (int algo, unsigned char *result, size_t result_len,
112
               const unsigned char *scalar, size_t scalar_len,
113
               const unsigned char *point, size_t point_len)
114
0
{
115
0
  const char *curve = algo_to_curve (algo);
116
117
0
  return _gcry_ecc_curve_mul_point (curve, result, result_len,
118
0
                                    scalar, scalar_len, point, point_len);
119
0
}
120
121
122
gpg_err_code_t
123
_gcry_ecc_raw_keypair (int algo, void *pubkey, size_t pubkey_len,
124
                       void *seckey, size_t seckey_len)
125
0
{
126
0
  const char *curve = algo_to_curve (algo);
127
128
0
  return _gcry_ecc_curve_keypair (curve,
129
0
                                  pubkey, pubkey_len, seckey, seckey_len);
130
0
}
131
132
gpg_err_code_t
133
_gcry_ecc_raw_encap (int algo, const void *pubkey, size_t pubkey_len,
134
                     void *ciphertext, size_t ciphertext_len,
135
                     void *shared, size_t shared_len)
136
0
{
137
0
  gpg_err_code_t err;
138
0
  unsigned char seckey_ephemeral[ECC_SECKEY_LEN_MAX];
139
0
  void *pubkey_ephemeral = ciphertext;
140
0
  size_t seckey_len;
141
142
0
  if (ciphertext_len != pubkey_len)
143
0
    return GPG_ERR_INV_VALUE;
144
145
0
  seckey_len = algo_to_seckey_len (algo);
146
0
  err = _gcry_ecc_raw_keypair (algo, pubkey_ephemeral, pubkey_len,
147
0
                               seckey_ephemeral, seckey_len);
148
0
  if (err)
149
0
    return err;
150
151
  /* Do ECDH.  */
152
0
  return ecc_mul_point (algo, shared, shared_len, seckey_ephemeral, seckey_len,
153
0
                        pubkey, pubkey_len);
154
0
}
155
156
gpg_err_code_t
157
_gcry_ecc_raw_decap (int algo, const void *seckey, size_t seckey_len,
158
                     const void *ciphertext, size_t ciphertext_len,
159
                     void *shared, size_t shared_len)
160
0
{
161
  /* Do ECDH.  */
162
0
  return ecc_mul_point (algo, shared, shared_len, seckey, seckey_len,
163
0
                        ciphertext, ciphertext_len);
164
0
}
165
166
167
enum
168
  {
169
    DHKEM_X25519_HKDF_SHA256 = 0x20, /* Defined in RFC 9180.  */
170
    DHKEM_X448_HKDF_SHA512   = 0x21
171
  };
172
173
static gpg_err_code_t
174
ecc_dhkem_kdf (int kem_algo, size_t ecc_len,
175
               const unsigned char *ecdh, const unsigned char *ciphertext,
176
               const unsigned char *pubkey, void *shared)
177
0
{
178
0
  gpg_err_code_t err;
179
0
  unsigned char *p;
180
0
  unsigned char labeled_ikm[7+5+7+ECC_PUBKEY_LEN_MAX];
181
0
  int labeled_ikm_size;
182
0
  unsigned char labeled_info[2+7+5+13+2*ECC_PUBKEY_LEN_MAX];
183
0
  int labeled_info_size;
184
0
  gcry_kdf_hd_t hd;
185
0
  unsigned long param[1];
186
0
  int macalgo;
187
0
  int mac_len;
188
189
0
  if (kem_algo == DHKEM_X25519_HKDF_SHA256)
190
0
    macalgo = GCRY_MAC_HMAC_SHA256;
191
0
  else if (kem_algo == DHKEM_X448_HKDF_SHA512)
192
0
    macalgo = GCRY_MAC_HMAC_SHA512;
193
0
  else
194
0
    return GPG_ERR_UNKNOWN_ALGORITHM;
195
196
0
  mac_len = _gcry_mac_get_algo_maclen (macalgo);
197
0
  param[0] = mac_len;
198
0
  labeled_ikm_size = 7+5+7+ecc_len;
199
0
  labeled_info_size = 2+7+5+13+ecc_len*2;
200
201
0
  p = labeled_ikm;
202
0
  memcpy (p, "HPKE-v1", 7);
203
0
  p += 7;
204
0
  memcpy (p, "KEM", 3);
205
0
  p[3] = 0;
206
0
  p[4] = kem_algo;
207
0
  p += 5;
208
0
  memcpy (p, "eae_prk", 7);
209
0
  p += 7;
210
0
  memcpy (p, ecdh, ecc_len);
211
212
0
  p = labeled_info;
213
  /* length */
214
0
  p[0] = 0;
215
0
  p[1] = mac_len;
216
0
  p += 2;
217
0
  memcpy (p, "HPKE-v1", 7);
218
0
  p += 7;
219
0
  memcpy (p, "KEM", 3);
220
0
  p[3] = 0;
221
0
  p[4] = kem_algo;
222
0
  p += 5;
223
0
  memcpy (p, "shared_secret", 13);
224
0
  p += 13;
225
  /* kem_context */
226
0
  memcpy (p, ciphertext, ecc_len);
227
0
  p += ecc_len;
228
0
  memcpy (p, pubkey, ecc_len);
229
0
  p += ecc_len;
230
231
0
  err = _gcry_kdf_open (&hd, GCRY_KDF_HKDF, macalgo, param, 1,
232
0
                        labeled_ikm, labeled_ikm_size,
233
0
                        NULL, 0, NULL, 0, labeled_info, labeled_info_size);
234
0
  if (err)
235
0
    return err;
236
237
0
  err = _gcry_kdf_compute (hd, NULL);
238
0
  if (!err)
239
0
    err = _gcry_kdf_final (hd, mac_len, shared);
240
0
  _gcry_kdf_close (hd);
241
0
  return err;
242
0
}
243
244
245
gpg_err_code_t
246
_gcry_ecc_dhkem_encap (int algo, const void *pubkey, void *ciphertext,
247
                       void *shared)
248
0
{
249
0
  gpg_err_code_t err;
250
0
  unsigned char ecdh[ECC_PUBKEY_LEN_MAX];
251
0
  unsigned char seckey_ephemeral[ECC_SECKEY_LEN_MAX];
252
0
  void *pubkey_ephemeral = ciphertext;
253
0
  int curveid;
254
0
  int kem_algo;
255
0
  size_t ecc_len;
256
257
0
  if (algo == GCRY_KEM_DHKEM25519)
258
0
    {
259
0
      curveid = GCRY_ECC_CURVE25519;
260
0
      kem_algo = DHKEM_X25519_HKDF_SHA256;
261
0
    }
262
0
  else if (algo == GCRY_KEM_DHKEM448)
263
0
    {
264
0
      curveid = GCRY_ECC_CURVE448;
265
0
      kem_algo = DHKEM_X448_HKDF_SHA512;
266
0
    }
267
0
  else
268
0
    return GPG_ERR_UNKNOWN_ALGORITHM;
269
270
0
  ecc_len = _gcry_ecc_get_algo_keylen (curveid);
271
272
0
  err = _gcry_ecc_raw_keypair (algo, pubkey_ephemeral, ecc_len,
273
0
                               seckey_ephemeral, ecc_len);
274
0
  if (err)
275
0
    return err;
276
277
  /* Do ECDH.  */
278
0
  err = ecc_mul_point (algo, ecdh, ecc_len, seckey_ephemeral, ecc_len,
279
0
                       pubkey, ecc_len);
280
0
  if (err)
281
0
    return err;
282
283
0
  return ecc_dhkem_kdf (kem_algo, ecc_len, ecdh, ciphertext, pubkey, shared);
284
0
}
285
286
gpg_err_code_t
287
_gcry_ecc_dhkem_decap (int algo, const void *seckey, const void *ciphertext,
288
                       void *shared, const void *optional)
289
0
{
290
0
  gpg_err_code_t err;
291
0
  unsigned char ecdh[ECC_PUBKEY_LEN_MAX];
292
0
  unsigned char pubkey_computed[ECC_PUBKEY_LEN_MAX];
293
0
  const unsigned char *pubkey;
294
0
  int curveid;
295
0
  int kem_algo;
296
0
  size_t ecc_len;
297
298
0
  if (algo == GCRY_KEM_DHKEM25519)
299
0
    {
300
0
      curveid = GCRY_ECC_CURVE25519;
301
0
      kem_algo = DHKEM_X25519_HKDF_SHA256;
302
0
    }
303
0
  else if (algo == GCRY_KEM_DHKEM448)
304
0
    {
305
0
      curveid = GCRY_ECC_CURVE448;
306
0
      kem_algo = DHKEM_X448_HKDF_SHA512;
307
0
    }
308
0
  else
309
0
    return GPG_ERR_UNKNOWN_ALGORITHM;
310
311
0
  ecc_len = _gcry_ecc_get_algo_keylen (curveid);
312
313
0
  if (optional)
314
0
    pubkey = optional;
315
0
  else
316
0
    {
317
0
      err = ecc_mul_point (algo, pubkey_computed, ecc_len, seckey, ecc_len,
318
0
                           NULL, ecc_len);
319
0
      if (err)
320
0
        return err;
321
322
0
      pubkey = pubkey_computed;
323
0
    }
324
325
  /* Do ECDH.  */
326
0
  err = ecc_mul_point (algo, ecdh, ecc_len, seckey, ecc_len,
327
0
                       ciphertext, ecc_len);
328
0
  if (err)
329
0
    return err;
330
331
0
  return ecc_dhkem_kdf (kem_algo, ecc_len, ecdh, ciphertext, pubkey, shared);
332
0
}