/src/libgcrypt/cipher/kem.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* kem.c - Key Encapsulation Mechanisms |
2 | | * Copyright (C) 2023 Simon Josefsson <simon@josefsson.org> |
3 | | * Copyright (C) 2023 g10 Code GmbH |
4 | | * |
5 | | * This file is part of Libgcrypt. |
6 | | * |
7 | | * Libgcrypt is free software; you can redistribute it and/or modify |
8 | | * it under the terms of the GNU Lesser General Public License as |
9 | | * published by the Free Software Foundation; either version 2.1 of |
10 | | * the License, or (at your option) any later version. |
11 | | * |
12 | | * Libgcrypt is distributed in the hope that it will be useful, |
13 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | | * GNU Lesser General Public License for more details. |
16 | | * |
17 | | * You should have received a copy of the GNU Lesser General Public |
18 | | * License along with this program; if not, see <https://www.gnu.org/licenses/>. |
19 | | * SPDX-License-Identifier: LGPL-2.1-or-later |
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 | | #include "sntrup761.h" |
31 | | #include "mceliece6688128f.h" |
32 | | #include "kyber.h" |
33 | | #include "kem-ecc.h" |
34 | | |
35 | | |
36 | | /* Information about the the KEM algoithms for use by the s-expression |
37 | | * interface. */ |
38 | | static const struct |
39 | | { |
40 | | const char *name; /* Name of the algo. */ |
41 | | unsigned int namelen; /* Only here to avoid strlen calls. */ |
42 | | int algo; /* KEM algo number. */ |
43 | | unsigned int nbits; /* Number of bits. */ |
44 | | unsigned int fips:1; /* True if this is a FIPS140-3 approved KEM. */ |
45 | | int pubkey_len; /* Length of the public key. */ |
46 | | int seckey_len; /* Length of the secret key. */ |
47 | | } kem_infos[] = |
48 | | { |
49 | | { "sntrup761", 9, GCRY_KEM_SNTRUP761, 761, 0, |
50 | | GCRY_KEM_SNTRUP761_PUBKEY_LEN, GCRY_KEM_SNTRUP761_SECKEY_LEN }, |
51 | | { "kyber512", 8, GCRY_KEM_MLKEM512, 512, 0, |
52 | | GCRY_KEM_MLKEM512_PUBKEY_LEN, GCRY_KEM_MLKEM512_SECKEY_LEN }, |
53 | | { "kyber768", 8, GCRY_KEM_MLKEM768, 768, 1, |
54 | | GCRY_KEM_MLKEM768_PUBKEY_LEN, GCRY_KEM_MLKEM768_SECKEY_LEN }, |
55 | | { "kyber1024", 9, GCRY_KEM_MLKEM1024, 1024, 1, |
56 | | GCRY_KEM_MLKEM1024_PUBKEY_LEN, GCRY_KEM_MLKEM1024_SECKEY_LEN }, |
57 | | { NULL } |
58 | | }; |
59 | | |
60 | | /* This is a short version of kem_infos from above. It is required |
61 | | * for the algoithm module interface. Keep in sync. */ |
62 | | static const char *kem_names[] = |
63 | | { |
64 | | "sntrup761", |
65 | | "kyber512", |
66 | | "kyber768", |
67 | | "kyber1024", |
68 | | NULL |
69 | | }; |
70 | | |
71 | | |
72 | | |
73 | | |
74 | | /* Helper for sntrup761. */ |
75 | | static void |
76 | | sntrup761_random (void *ctx, size_t length, uint8_t *dst) |
77 | 0 | { |
78 | 0 | (void)ctx; |
79 | |
|
80 | 0 | _gcry_randomize (dst, length, GCRY_STRONG_RANDOM); |
81 | 0 | } |
82 | | |
83 | | |
84 | | gcry_err_code_t |
85 | | _gcry_kem_genkey (int algo, |
86 | | void *pubkey, size_t pubkey_len, |
87 | | void *seckey, size_t seckey_len, |
88 | | const void *optional, size_t optional_len) |
89 | 0 | { |
90 | 0 | switch (algo) |
91 | 0 | { |
92 | 0 | case GCRY_KEM_SNTRUP761: |
93 | 0 | if (seckey_len != GCRY_KEM_SNTRUP761_SECKEY_LEN |
94 | 0 | || pubkey_len != GCRY_KEM_SNTRUP761_PUBKEY_LEN) |
95 | 0 | return GPG_ERR_INV_ARG; |
96 | 0 | sntrup761_keypair (pubkey, seckey, NULL, sntrup761_random); |
97 | 0 | return 0; |
98 | | |
99 | 0 | case GCRY_KEM_CM6688128F: |
100 | 0 | mceliece6688128f_keypair (pubkey, seckey); |
101 | 0 | return 0; |
102 | | |
103 | 0 | case GCRY_KEM_MLKEM512: |
104 | 0 | if (seckey_len != GCRY_KEM_MLKEM512_SECKEY_LEN |
105 | 0 | || pubkey_len != GCRY_KEM_MLKEM512_PUBKEY_LEN |
106 | 0 | || (optional && optional_len != GCRY_KEM_MLKEM_RANDOM_LEN*2)) |
107 | 0 | return GPG_ERR_INV_ARG; |
108 | 0 | kyber_keypair (algo, pubkey, seckey, optional); |
109 | 0 | return 0; |
110 | | |
111 | 0 | case GCRY_KEM_MLKEM768: |
112 | 0 | if (seckey_len != GCRY_KEM_MLKEM768_SECKEY_LEN |
113 | 0 | || pubkey_len != GCRY_KEM_MLKEM768_PUBKEY_LEN |
114 | 0 | || (optional && optional_len != GCRY_KEM_MLKEM_RANDOM_LEN*2)) |
115 | 0 | return GPG_ERR_INV_ARG; |
116 | 0 | kyber_keypair (algo, pubkey, seckey, optional); |
117 | 0 | return 0; |
118 | | |
119 | 0 | case GCRY_KEM_MLKEM1024: |
120 | 0 | if (seckey_len != GCRY_KEM_MLKEM1024_SECKEY_LEN |
121 | 0 | || pubkey_len != GCRY_KEM_MLKEM1024_PUBKEY_LEN |
122 | 0 | || (optional && optional_len != GCRY_KEM_MLKEM_RANDOM_LEN*2)) |
123 | 0 | return GPG_ERR_INV_ARG; |
124 | 0 | kyber_keypair (algo, pubkey, seckey, optional); |
125 | 0 | return 0; |
126 | | |
127 | 0 | case GCRY_KEM_RAW_X25519: |
128 | 0 | case GCRY_KEM_RAW_X448: |
129 | 0 | case GCRY_KEM_RAW_BP256: |
130 | 0 | case GCRY_KEM_RAW_BP384: |
131 | 0 | case GCRY_KEM_RAW_BP512: |
132 | 0 | case GCRY_KEM_RAW_P256R1: |
133 | 0 | case GCRY_KEM_RAW_P384R1: |
134 | 0 | case GCRY_KEM_RAW_P521R1: |
135 | 0 | case GCRY_KEM_DHKEM25519: |
136 | 0 | case GCRY_KEM_DHKEM448: |
137 | 0 | return _gcry_ecc_raw_keypair (algo, pubkey, pubkey_len, |
138 | 0 | seckey, seckey_len); |
139 | | |
140 | 0 | default: |
141 | 0 | return GPG_ERR_UNKNOWN_ALGORITHM; |
142 | 0 | } |
143 | | |
144 | 0 | return GPG_ERR_UNKNOWN_ALGORITHM; |
145 | 0 | } |
146 | | |
147 | | |
148 | | gcry_err_code_t |
149 | | _gcry_kem_encap (int algo, |
150 | | const void *pubkey, size_t pubkey_len, |
151 | | void *ciphertext, size_t ciphertext_len, |
152 | | void *shared, size_t shared_len, |
153 | | const void *optional, size_t optional_len) |
154 | 0 | { |
155 | 0 | switch (algo) |
156 | 0 | { |
157 | 0 | case GCRY_KEM_SNTRUP761: |
158 | 0 | if (optional != NULL || optional_len != 0) |
159 | 0 | return GPG_ERR_INV_VALUE; |
160 | 0 | if (pubkey_len != GCRY_KEM_SNTRUP761_PUBKEY_LEN |
161 | 0 | || ciphertext_len != GCRY_KEM_SNTRUP761_ENCAPS_LEN |
162 | 0 | || shared_len != GCRY_KEM_SNTRUP761_SHARED_LEN) |
163 | 0 | return GPG_ERR_INV_VALUE; |
164 | 0 | sntrup761_enc (ciphertext, shared, pubkey, NULL, sntrup761_random); |
165 | 0 | return 0; |
166 | | |
167 | 0 | case GCRY_KEM_CM6688128F: |
168 | 0 | if (optional != NULL) |
169 | 0 | return GPG_ERR_INV_VALUE; |
170 | 0 | mceliece6688128f_enc (ciphertext, shared, pubkey); |
171 | 0 | return 0; |
172 | | |
173 | 0 | case GCRY_KEM_MLKEM512: |
174 | 0 | case GCRY_KEM_MLKEM768: |
175 | 0 | case GCRY_KEM_MLKEM1024: |
176 | 0 | if (optional && optional_len != GCRY_KEM_MLKEM_RANDOM_LEN) |
177 | 0 | return GPG_ERR_INV_VALUE; |
178 | 0 | kyber_encap (algo, ciphertext, shared, pubkey, optional); |
179 | 0 | return 0; |
180 | | |
181 | 0 | case GCRY_KEM_RAW_X25519: |
182 | 0 | case GCRY_KEM_RAW_X448: |
183 | 0 | case GCRY_KEM_RAW_BP256: |
184 | 0 | case GCRY_KEM_RAW_BP384: |
185 | 0 | case GCRY_KEM_RAW_BP512: |
186 | 0 | case GCRY_KEM_RAW_P256R1: |
187 | 0 | case GCRY_KEM_RAW_P384R1: |
188 | 0 | case GCRY_KEM_RAW_P521R1: |
189 | 0 | if (optional != NULL) |
190 | 0 | return GPG_ERR_INV_VALUE; |
191 | 0 | return _gcry_ecc_raw_encap (algo, pubkey, pubkey_len, |
192 | 0 | ciphertext, ciphertext_len, |
193 | 0 | shared, shared_len); |
194 | | |
195 | 0 | case GCRY_KEM_DHKEM25519: |
196 | 0 | case GCRY_KEM_DHKEM448: |
197 | 0 | if (optional != NULL) |
198 | 0 | return GPG_ERR_INV_VALUE; |
199 | 0 | return _gcry_ecc_dhkem_encap (algo, pubkey, ciphertext, shared); |
200 | | |
201 | 0 | default: |
202 | 0 | return GPG_ERR_UNKNOWN_ALGORITHM; |
203 | 0 | } |
204 | 0 | return GPG_ERR_UNKNOWN_ALGORITHM; |
205 | 0 | } |
206 | | |
207 | | |
208 | | gcry_err_code_t |
209 | | _gcry_kem_decap (int algo, |
210 | | const void *seckey, size_t seckey_len, |
211 | | const void *ciphertext, size_t ciphertext_len, |
212 | | void *shared, size_t shared_len, |
213 | | const void *optional, size_t optional_len) |
214 | 0 | { |
215 | 0 | switch (algo) |
216 | 0 | { |
217 | 0 | case GCRY_KEM_SNTRUP761: |
218 | 0 | if (optional != NULL || optional_len != 0) |
219 | 0 | return GPG_ERR_INV_VALUE; |
220 | 0 | if (seckey_len != GCRY_KEM_SNTRUP761_SECKEY_LEN |
221 | 0 | || ciphertext_len != GCRY_KEM_SNTRUP761_ENCAPS_LEN |
222 | 0 | || shared_len != GCRY_KEM_SNTRUP761_SHARED_LEN) |
223 | 0 | return GPG_ERR_INV_VALUE; |
224 | 0 | sntrup761_dec (shared, ciphertext, seckey); |
225 | 0 | return 0; |
226 | | |
227 | 0 | case GCRY_KEM_CM6688128F: |
228 | 0 | if (optional != NULL) |
229 | 0 | return GPG_ERR_INV_VALUE; |
230 | 0 | mceliece6688128f_dec (shared, ciphertext, seckey); |
231 | 0 | return 0; |
232 | | |
233 | 0 | case GCRY_KEM_MLKEM512: |
234 | 0 | case GCRY_KEM_MLKEM768: |
235 | 0 | case GCRY_KEM_MLKEM1024: |
236 | 0 | if (optional != NULL) |
237 | 0 | return GPG_ERR_INV_VALUE; |
238 | 0 | kyber_decap (algo, shared, ciphertext, seckey); |
239 | 0 | return 0; |
240 | | |
241 | 0 | case GCRY_KEM_RAW_X25519: |
242 | 0 | case GCRY_KEM_RAW_X448: |
243 | 0 | case GCRY_KEM_RAW_BP256: |
244 | 0 | case GCRY_KEM_RAW_BP384: |
245 | 0 | case GCRY_KEM_RAW_BP512: |
246 | 0 | case GCRY_KEM_RAW_P256R1: |
247 | 0 | case GCRY_KEM_RAW_P384R1: |
248 | 0 | case GCRY_KEM_RAW_P521R1: |
249 | 0 | if (optional != NULL) |
250 | 0 | return GPG_ERR_INV_VALUE; |
251 | 0 | return _gcry_ecc_raw_decap (algo, seckey, seckey_len, |
252 | 0 | ciphertext, ciphertext_len, |
253 | 0 | shared, shared_len); |
254 | | |
255 | 0 | case GCRY_KEM_DHKEM25519: |
256 | 0 | case GCRY_KEM_DHKEM448: |
257 | 0 | return _gcry_ecc_dhkem_decap (algo, seckey, ciphertext, shared, |
258 | 0 | optional); |
259 | | |
260 | 0 | default: |
261 | 0 | return GPG_ERR_UNKNOWN_ALGORITHM; |
262 | 0 | } |
263 | 0 | return GPG_ERR_UNKNOWN_ALGORITHM; |
264 | 0 | } |
265 | | |
266 | | |
267 | | |
268 | | /* Generate a KEM keypair using the s-expression interface. The |
269 | | * GENPARAMS is prety simple in this case because it has only the |
270 | | * algorithm name. For example: |
271 | | * (kyber768) |
272 | | */ |
273 | | static gcry_err_code_t |
274 | | kem_generate (const gcry_sexp_t genparms, gcry_sexp_t *r_skey) |
275 | 0 | { |
276 | 0 | gpg_err_code_t ec; |
277 | 0 | const char *algo; |
278 | 0 | size_t algolen; |
279 | 0 | const char *name; |
280 | 0 | int i; |
281 | 0 | int algoid; |
282 | 0 | void *pubkey = NULL; |
283 | 0 | void *seckey = NULL; |
284 | 0 | size_t pubkey_len, seckey_len; |
285 | |
|
286 | 0 | algo = sexp_nth_data (genparms, 0, &algolen); |
287 | 0 | if (!algo || !algolen) |
288 | 0 | return GPG_ERR_PUBKEY_ALGO; |
289 | 0 | for (i=0; (name=kem_infos[i].name); i++) |
290 | 0 | if (kem_infos[i].namelen == algolen && !memcmp (name, algo, algolen)) |
291 | 0 | break; |
292 | 0 | if (!name) |
293 | 0 | return GPG_ERR_WRONG_PUBKEY_ALGO; |
294 | 0 | algoid = kem_infos[i].algo; |
295 | 0 | pubkey_len = kem_infos[i].pubkey_len; |
296 | 0 | seckey_len = kem_infos[i].seckey_len; |
297 | | /* (from here on we can jump to leave for cleanup) */ |
298 | | |
299 | | /* Allocate buffers for the created key. */ |
300 | 0 | seckey = xtrycalloc_secure (1, seckey_len); |
301 | 0 | if (!seckey) |
302 | 0 | { |
303 | 0 | ec = gpg_err_code_from_syserror (); |
304 | 0 | goto leave; |
305 | 0 | } |
306 | 0 | pubkey = xtrycalloc (1, pubkey_len); |
307 | 0 | if (!pubkey) |
308 | 0 | { |
309 | 0 | ec = gpg_err_code_from_syserror (); |
310 | 0 | goto leave; |
311 | 0 | } |
312 | | |
313 | | /* Generate key. */ |
314 | 0 | ec = _gcry_kem_genkey (algoid, pubkey, pubkey_len, seckey, seckey_len, |
315 | 0 | NULL, 0); |
316 | 0 | if (ec) |
317 | 0 | goto leave; |
318 | | |
319 | | /* Put the key into an s-expression. */ |
320 | 0 | ec = sexp_build (r_skey, NULL, |
321 | 0 | "(key-data" |
322 | 0 | " (public-key" |
323 | 0 | " (%s(p%b)))" |
324 | 0 | " (private-key" |
325 | 0 | " (%s(p%b)(s%b))))", |
326 | 0 | name, |
327 | 0 | (int)pubkey_len, pubkey, |
328 | 0 | name, |
329 | 0 | (int)pubkey_len, pubkey, |
330 | 0 | (int)seckey_len, seckey); |
331 | | |
332 | | |
333 | | /* FIXME: Add FIPS selftest. */ |
334 | |
|
335 | 0 | leave: |
336 | 0 | if (seckey) |
337 | 0 | { |
338 | 0 | wipememory (seckey, seckey_len); |
339 | 0 | xfree (seckey); |
340 | 0 | } |
341 | 0 | xfree (pubkey); |
342 | 0 | return ec; |
343 | 0 | } |
344 | | |
345 | | |
346 | | /* Compute a keygrip. MD is the hash context which we are going to |
347 | | * update. KEYPARAM is an S-expression with the key parameters, this |
348 | | * is usually a public key but may also be a secret key. An example |
349 | | * of such an S-expression is: |
350 | | * |
351 | | * (kyber768 |
352 | | * (p #4243...#) |
353 | | * (s #1718...#)) |
354 | | * |
355 | | * What we hash is the algorithm name, \x00 and the value of p. |
356 | | * Including the algorithm name allows us to see a different key |
357 | | * despite that it uses the same parameters. Whether this is a good |
358 | | * decision is not clear - but it should not harm. |
359 | | */ |
360 | | static gpg_err_code_t |
361 | | kem_compute_keygrip (gcry_md_hd_t md, gcry_sexp_t keyparam) |
362 | 0 | { |
363 | 0 | gcry_sexp_t l1; |
364 | 0 | const char *algo, *data; |
365 | 0 | size_t algolen, datalen; |
366 | 0 | const char *name; |
367 | 0 | int i; |
368 | |
|
369 | 0 | algo = sexp_nth_data (keyparam, 0, &algolen); |
370 | 0 | if (!algo || !algolen) |
371 | 0 | return GPG_ERR_PUBKEY_ALGO; |
372 | 0 | for (i=0; (name=kem_infos[i].name); i++) |
373 | 0 | if (kem_infos[i].namelen == algolen && !memcmp (name, algo, algolen)) |
374 | 0 | break; |
375 | 0 | if (!name) |
376 | 0 | return GPG_ERR_WRONG_PUBKEY_ALGO; |
377 | | |
378 | 0 | _gcry_md_write (md, name, algolen+1); /* (also hash the nul) */ |
379 | |
|
380 | 0 | l1 = sexp_find_token (keyparam, "p", 1); |
381 | 0 | if (!l1) |
382 | 0 | return GPG_ERR_NO_OBJ; |
383 | | |
384 | 0 | data = sexp_nth_data (l1, 1, &datalen); |
385 | 0 | if (!data) |
386 | 0 | { |
387 | 0 | sexp_release (l1); |
388 | 0 | return GPG_ERR_NO_OBJ; |
389 | 0 | } |
390 | | |
391 | 0 | _gcry_md_write (md, data, datalen); |
392 | 0 | sexp_release (l1); |
393 | |
|
394 | 0 | return 0; |
395 | 0 | } |
396 | | |
397 | | |
398 | | /* Return the number of bits for the key described by PARMS. On error |
399 | | * 0 is returned. */ |
400 | | static unsigned int |
401 | | kem_get_nbits (gcry_sexp_t keyparam) |
402 | 0 | { |
403 | 0 | const char *algo; |
404 | 0 | size_t algolen; |
405 | 0 | const char *name; |
406 | 0 | int i; |
407 | |
|
408 | 0 | algo = sexp_nth_data (keyparam, 0, &algolen); |
409 | 0 | if (!algo || !algolen) |
410 | 0 | return 0; /* GPG_ERR_PUBKEY_ALGO */ |
411 | 0 | for (i=0; (name=kem_infos[i].name); i++) |
412 | 0 | if (kem_infos[i].namelen == algolen && !memcmp (name, algo, algolen)) |
413 | 0 | break; |
414 | 0 | if (!name) |
415 | 0 | return 0; /* GPG_ERR_WRONG_PUBKEY_ALGO */ |
416 | | |
417 | 0 | return kem_infos[i].nbits; |
418 | 0 | } |
419 | | |
420 | | |
421 | | /* Generic structure to represent some KEM algorithms in our public |
422 | | * key system. */ |
423 | | gcry_pk_spec_t _gcry_pubkey_spec_kem = |
424 | | { |
425 | | GCRY_PK_KEM, { 0, 0 }, |
426 | | GCRY_PK_USAGE_ENCR, |
427 | | "KEM", kem_names, |
428 | | "p", "s", "k", "", "p", |
429 | | kem_generate, |
430 | | NULL, /* kem_check_secret_key */ |
431 | | NULL, /* encrypt_raw - Use gcry_kem_encap instead. */ |
432 | | NULL, /* decrypt_raw - Use gcry_kem_decap unstead. */ |
433 | | NULL, /* sign */ |
434 | | NULL, /* verify */ |
435 | | kem_get_nbits, |
436 | | NULL, /* selftests */ |
437 | | kem_compute_keygrip, |
438 | | NULL, /* get_curve */ |
439 | | NULL /* get_curve_param */ |
440 | | }; |