Line | Count | Source |
1 | | /* pkglue.c - public key operations glue code |
2 | | * Copyright (C) 2000, 2003, 2010 Free Software Foundation, Inc. |
3 | | * Copyright (C) 2014 Werner Koch |
4 | | * Copyright (C) 2024 g10 Code GmbH. |
5 | | * |
6 | | * This file is part of GnuPG. |
7 | | * |
8 | | * GnuPG is free software; you can redistribute it and/or modify |
9 | | * it under the terms of the GNU General Public License as published by |
10 | | * the Free Software Foundation; either version 3 of the License, or |
11 | | * (at your option) any later version. |
12 | | * |
13 | | * GnuPG is distributed in the hope that it will be useful, |
14 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | | * GNU General Public License for more details. |
17 | | * |
18 | | * You should have received a copy of the GNU General Public License |
19 | | * along with this program; if not, see <https://www.gnu.org/licenses/>. |
20 | | * SPDX-License-Identifier: GPL-3.0-or-later |
21 | | */ |
22 | | |
23 | | #include <config.h> |
24 | | #include <stdio.h> |
25 | | #include <stdlib.h> |
26 | | #include <string.h> |
27 | | #include <errno.h> |
28 | | |
29 | | #include "gpg.h" |
30 | | #include "../common/util.h" |
31 | | #include "pkglue.h" |
32 | | #include "main.h" |
33 | | #include "options.h" |
34 | | |
35 | | |
36 | | /* FIXME: Better change the function name because mpi_ is used by |
37 | | gcrypt macros. */ |
38 | | gcry_mpi_t |
39 | | get_mpi_from_sexp (gcry_sexp_t sexp, const char *item, int mpifmt) |
40 | 0 | { |
41 | 0 | gcry_sexp_t list; |
42 | 0 | gcry_mpi_t data; |
43 | |
|
44 | 0 | list = gcry_sexp_find_token (sexp, item, 0); |
45 | 0 | log_assert (list); |
46 | 0 | data = gcry_sexp_nth_mpi (list, 1, mpifmt); |
47 | 0 | log_assert (data); |
48 | 0 | gcry_sexp_release (list); |
49 | 0 | return data; |
50 | 0 | } |
51 | | |
52 | | |
53 | | /* This is the same as get_mpi_from_sexp but removes a 0x40 prefix |
54 | | * from the requested parameter. An opaque MPI is returned on |
55 | | * success, NULL on error. */ |
56 | | gcry_mpi_t |
57 | | get_mpi_from_sexp_strip_0x40 (gcry_sexp_t sexp, const char *item) |
58 | 0 | { |
59 | 0 | gcry_sexp_t list; |
60 | 0 | size_t len; |
61 | 0 | const char *p; |
62 | 0 | char *buffer; |
63 | 0 | gcry_mpi_t result = NULL; |
64 | |
|
65 | 0 | list = gcry_sexp_find_token (sexp, item, 0); |
66 | 0 | if (!list) |
67 | 0 | return NULL; |
68 | | |
69 | 0 | p = gcry_sexp_nth_data (list, 1, &len); |
70 | 0 | if (!p || !len) |
71 | 0 | goto leave; |
72 | | |
73 | | /* If we have a parameter of at least 256 bits with an odd length in |
74 | | * octets and the first octet is 0x40 we remove that octet. The |
75 | | * 0x40 indicates native point format and is for example used when |
76 | | * we create Curve25519 key. */ |
77 | 0 | if ((len & 1) && len > 32 && *p == 0x40) |
78 | 0 | { |
79 | 0 | p++; |
80 | 0 | len--; |
81 | 0 | } |
82 | 0 | buffer = xtrymalloc (len); |
83 | 0 | if (!buffer) |
84 | 0 | goto leave; |
85 | 0 | memcpy (buffer, p, len); |
86 | 0 | result = gcry_mpi_set_opaque (NULL, buffer, len*8); |
87 | 0 | buffer = NULL; |
88 | |
|
89 | 0 | leave: |
90 | 0 | gcry_sexp_release (list); |
91 | 0 | return result; |
92 | 0 | } |
93 | | |
94 | | |
95 | | /* Return an opaque MPI with the concatenated values of the "r" and |
96 | | * "s" parameters from SEXP. Return NULL on error*/ |
97 | | gcry_mpi_t |
98 | | get_r_s_mpi_from_sexp (gcry_sexp_t sexp) |
99 | 0 | { |
100 | 0 | gcry_sexp_t rlist, slist; |
101 | 0 | size_t rlen, slen; |
102 | 0 | const char *r, *s; |
103 | 0 | char *buffer; |
104 | 0 | gcry_mpi_t result = NULL; |
105 | |
|
106 | 0 | rlist = gcry_sexp_find_token (sexp, "r", 0); |
107 | 0 | if (!rlist) |
108 | 0 | return NULL; |
109 | 0 | slist = gcry_sexp_find_token (sexp, "s", 0); |
110 | 0 | if (!slist) |
111 | 0 | goto leave; |
112 | | |
113 | 0 | r = gcry_sexp_nth_data (rlist, 1, &rlen); |
114 | 0 | s = gcry_sexp_nth_data (slist, 1, &slen); |
115 | 0 | if (!r || !s || !rlen || !slen || rlen != slen) |
116 | 0 | goto leave; |
117 | | |
118 | 0 | buffer = xtrymalloc (rlen+slen); |
119 | 0 | if (!buffer) |
120 | 0 | goto leave; |
121 | 0 | memcpy (buffer, r, rlen); |
122 | 0 | memcpy (buffer+rlen, s, slen); |
123 | 0 | result = gcry_mpi_set_opaque (NULL, buffer, (rlen+slen)*8); |
124 | 0 | buffer = NULL; |
125 | |
|
126 | 0 | leave: |
127 | 0 | gcry_sexp_release (rlist); |
128 | 0 | gcry_sexp_release (slist); |
129 | 0 | return result; |
130 | 0 | } |
131 | | |
132 | | |
133 | | /* |
134 | | * SOS (Simply, Octet String) is an attempt to handle opaque octet |
135 | | * string in OpenPGP, where well-formed MPI cannot represent octet |
136 | | * string with leading zero octets. |
137 | | * |
138 | | * To retain maximum compatibility to existing MPI handling, SOS |
139 | | * has same structure, but allows leading zero octets. When there |
140 | | * is no leading zero octets, SOS representation is as same as MPI one. |
141 | | * With leading zero octets, NBITS is 8*(length of octets), regardless |
142 | | * of leading zero bits. |
143 | | */ |
144 | | /* Extract SOS representation from SEXP for PARAM, return the result |
145 | | * in R_SOS. It is represented by opaque MPI with GCRYMPI_FLAG_USER2 |
146 | | * flag. */ |
147 | | gpg_error_t |
148 | | sexp_extract_param_sos (gcry_sexp_t sexp, const char *param, gcry_mpi_t *r_sos) |
149 | 0 | { |
150 | 0 | gpg_error_t err; |
151 | 0 | gcry_sexp_t l2 = gcry_sexp_find_token (sexp, param, 0); |
152 | |
|
153 | 0 | *r_sos = NULL; |
154 | 0 | if (!l2) |
155 | 0 | err = gpg_error (GPG_ERR_NO_OBJ); |
156 | 0 | else |
157 | 0 | { |
158 | 0 | size_t buflen; |
159 | 0 | void *p0 = gcry_sexp_nth_buffer (l2, 1, &buflen); |
160 | |
|
161 | 0 | if (!p0) |
162 | 0 | err = gpg_error_from_syserror (); |
163 | 0 | else |
164 | 0 | { |
165 | 0 | gcry_mpi_t sos; |
166 | 0 | unsigned int nbits = buflen*8; |
167 | 0 | unsigned char *p = p0; |
168 | |
|
169 | 0 | if (*p && nbits >= 8 && !(*p & 0x80)) |
170 | 0 | if (--nbits >= 7 && !(*p & 0x40)) |
171 | 0 | if (--nbits >= 6 && !(*p & 0x20)) |
172 | 0 | if (--nbits >= 5 && !(*p & 0x10)) |
173 | 0 | if (--nbits >= 4 && !(*p & 0x08)) |
174 | 0 | if (--nbits >= 3 && !(*p & 0x04)) |
175 | 0 | if (--nbits >= 2 && !(*p & 0x02)) |
176 | 0 | if (--nbits >= 1 && !(*p & 0x01)) |
177 | 0 | --nbits; |
178 | |
|
179 | 0 | sos = gcry_mpi_set_opaque (NULL, p0, nbits); |
180 | 0 | if (sos) |
181 | 0 | { |
182 | 0 | gcry_mpi_set_flag (sos, GCRYMPI_FLAG_USER2); |
183 | 0 | *r_sos = sos; |
184 | 0 | err = 0; |
185 | 0 | } |
186 | 0 | else |
187 | 0 | err = gpg_error_from_syserror (); |
188 | 0 | } |
189 | 0 | gcry_sexp_release (l2); |
190 | 0 | } |
191 | |
|
192 | 0 | return err; |
193 | 0 | } |
194 | | |
195 | | |
196 | | /* "No leading zero octets" (nlz) version of the function above. |
197 | | * |
198 | | * This routine is used for backward compatibility to existing |
199 | | * implementation with the weird handling of little endian integer |
200 | | * representation with leading zero octets. For the sake of |
201 | | * "well-fomed" MPI, which is designed for big endian integer, leading |
202 | | * zero octets are removed when output, and they are recovered at |
203 | | * input. |
204 | | * |
205 | | * Extract SOS representation from SEXP for PARAM, removing leading |
206 | | * zeros, return the result in R_SOS. */ |
207 | | gpg_error_t |
208 | | sexp_extract_param_sos_nlz (gcry_sexp_t sexp, const char *param, |
209 | | gcry_mpi_t *r_sos) |
210 | 0 | { |
211 | 0 | gpg_error_t err; |
212 | 0 | gcry_sexp_t l2 = gcry_sexp_find_token (sexp, param, 0); |
213 | |
|
214 | 0 | *r_sos = NULL; |
215 | 0 | if (!l2) |
216 | 0 | err = gpg_error (GPG_ERR_NO_OBJ); |
217 | 0 | else |
218 | 0 | { |
219 | 0 | size_t buflen; |
220 | 0 | const void *p0 = gcry_sexp_nth_data (l2, 1, &buflen); |
221 | |
|
222 | 0 | if (!p0) |
223 | 0 | err = gpg_error_from_syserror (); |
224 | 0 | else |
225 | 0 | { |
226 | 0 | gcry_mpi_t sos; |
227 | 0 | unsigned int nbits = buflen*8; |
228 | 0 | const unsigned char *p = p0; |
229 | | |
230 | | /* Strip leading zero bits. */ |
231 | 0 | for (; nbits >= 8 && !*p; p++, nbits -= 8) |
232 | 0 | ; |
233 | |
|
234 | 0 | if (nbits >= 8 && !(*p & 0x80)) |
235 | 0 | if (--nbits >= 7 && !(*p & 0x40)) |
236 | 0 | if (--nbits >= 6 && !(*p & 0x20)) |
237 | 0 | if (--nbits >= 5 && !(*p & 0x10)) |
238 | 0 | if (--nbits >= 4 && !(*p & 0x08)) |
239 | 0 | if (--nbits >= 3 && !(*p & 0x04)) |
240 | 0 | if (--nbits >= 2 && !(*p & 0x02)) |
241 | 0 | if (--nbits >= 1 && !(*p & 0x01)) |
242 | 0 | --nbits; |
243 | |
|
244 | 0 | sos = gcry_mpi_set_opaque_copy (NULL, p, nbits); |
245 | 0 | if (sos) |
246 | 0 | { |
247 | 0 | gcry_mpi_set_flag (sos, GCRYMPI_FLAG_USER2); |
248 | 0 | *r_sos = sos; |
249 | 0 | err = 0; |
250 | 0 | } |
251 | 0 | else |
252 | 0 | err = gpg_error_from_syserror (); |
253 | 0 | } |
254 | 0 | gcry_sexp_release (l2); |
255 | 0 | } |
256 | |
|
257 | 0 | return err; |
258 | 0 | } |
259 | | |
260 | | |
261 | | /**************** |
262 | | * Emulate our old PK interface here - sometime in the future we might |
263 | | * change the internal design to directly fit to libgcrypt. |
264 | | */ |
265 | | int |
266 | | pk_verify (pubkey_algo_t pkalgo, gcry_mpi_t hash, |
267 | | gcry_mpi_t *data, gcry_mpi_t *pkey) |
268 | 3.23k | { |
269 | 3.23k | gcry_sexp_t s_sig, s_hash, s_pkey; |
270 | 3.23k | int rc; |
271 | | |
272 | | /* Make a sexp from pkey. */ |
273 | 3.23k | if (pkalgo == PUBKEY_ALGO_DSA) |
274 | 389 | { |
275 | 389 | rc = gcry_sexp_build (&s_pkey, NULL, |
276 | 389 | "(public-key(dsa(p%m)(q%m)(g%m)(y%m)))", |
277 | 389 | pkey[0], pkey[1], pkey[2], pkey[3]); |
278 | 389 | } |
279 | 2.85k | else if (pkalgo == PUBKEY_ALGO_ELGAMAL_E || pkalgo == PUBKEY_ALGO_ELGAMAL) |
280 | 304 | { |
281 | 304 | rc = gcry_sexp_build (&s_pkey, NULL, |
282 | 304 | "(public-key(elg(p%m)(g%m)(y%m)))", |
283 | 304 | pkey[0], pkey[1], pkey[2]); |
284 | 304 | } |
285 | 2.54k | else if (pkalgo == PUBKEY_ALGO_RSA || pkalgo == PUBKEY_ALGO_RSA_S) |
286 | 379 | { |
287 | 379 | rc = gcry_sexp_build (&s_pkey, NULL, |
288 | 379 | "(public-key(rsa(n%m)(e%m)))", pkey[0], pkey[1]); |
289 | 379 | } |
290 | 2.16k | else if (pkalgo == PUBKEY_ALGO_ECDSA) |
291 | 179 | { |
292 | 179 | char *curve = openpgp_oid_to_str (pkey[0]); |
293 | 179 | if (!curve) |
294 | 0 | rc = gpg_error_from_syserror (); |
295 | 179 | else |
296 | 179 | { |
297 | 179 | rc = gcry_sexp_build (&s_pkey, NULL, |
298 | 179 | "(public-key(ecdsa(curve %s)(q%m)))", |
299 | 179 | curve, pkey[1]); |
300 | 179 | xfree (curve); |
301 | 179 | } |
302 | 179 | } |
303 | 1.98k | else if (pkalgo == PUBKEY_ALGO_EDDSA) |
304 | 1.96k | { |
305 | 1.96k | char *curve = openpgp_oid_to_str (pkey[0]); |
306 | 1.96k | if (!curve) |
307 | 0 | rc = gpg_error_from_syserror (); |
308 | 1.96k | else |
309 | 1.96k | { |
310 | 1.96k | const char *fmt; |
311 | | |
312 | 1.96k | if (openpgp_oid_is_ed25519 (pkey[0])) |
313 | 1.96k | fmt = "(public-key(ecc(curve %s)(flags eddsa)(q%m)))"; |
314 | 0 | else |
315 | 0 | fmt = "(public-key(ecc(curve %s)(q%m)))"; |
316 | | |
317 | 1.96k | rc = gcry_sexp_build (&s_pkey, NULL, fmt, curve, pkey[1]); |
318 | 1.96k | xfree (curve); |
319 | 1.96k | } |
320 | 1.96k | } |
321 | 22 | else if (pkalgo == PUBKEY_ALGO_ED25519) |
322 | 0 | { |
323 | 0 | rc = gcry_sexp_build (&s_pkey, NULL, |
324 | 0 | "(public-key(ecc(curve Ed25519)" |
325 | 0 | "(flags eddsa)(q%m)))", |
326 | 0 | pkey[0]); |
327 | 0 | } |
328 | 22 | else |
329 | 22 | return GPG_ERR_PUBKEY_ALGO; |
330 | | |
331 | 3.21k | if (rc) |
332 | 0 | BUG (); /* gcry_sexp_build should never fail. */ |
333 | | |
334 | | /* Put hash into a S-Exp s_hash. */ |
335 | 3.21k | if (pkalgo == PUBKEY_ALGO_EDDSA) |
336 | 1.96k | { |
337 | 1.96k | const char *fmt; |
338 | | |
339 | 1.96k | if (openpgp_oid_is_ed25519 (pkey[0])) |
340 | 1.96k | fmt = "(data(flags eddsa)(hash-algo sha512)(value %m))"; |
341 | 0 | else |
342 | 0 | fmt = "(data(value %m))"; |
343 | | |
344 | 1.96k | if (gcry_sexp_build (&s_hash, NULL, fmt, hash)) |
345 | 0 | BUG (); /* gcry_sexp_build should never fail. */ |
346 | 1.96k | } |
347 | 1.25k | else if (pkalgo == PUBKEY_ALGO_ED25519) |
348 | 0 | { |
349 | 0 | if (gcry_sexp_build (&s_hash, NULL, |
350 | 0 | "(data(flags eddsa)(hash-algo sha512)(value %m))", |
351 | 0 | hash)) |
352 | 0 | BUG (); /* gcry_sexp_build should never fail. */ |
353 | 0 | } |
354 | 1.25k | else |
355 | 1.25k | { |
356 | 1.25k | if (gcry_sexp_build (&s_hash, NULL, "%m", hash)) |
357 | 0 | BUG (); /* gcry_sexp_build should never fail. */ |
358 | 1.25k | } |
359 | | |
360 | | /* Put data into a S-Exp s_sig. */ |
361 | 3.21k | s_sig = NULL; |
362 | 3.21k | if (pkalgo == PUBKEY_ALGO_DSA) |
363 | 389 | { |
364 | 389 | if (!data[0] || !data[1]) |
365 | 76 | rc = gpg_error (GPG_ERR_BAD_MPI); |
366 | 313 | else |
367 | 313 | rc = gcry_sexp_build (&s_sig, NULL, |
368 | 313 | "(sig-val(dsa(r%m)(s%m)))", data[0], data[1]); |
369 | 389 | } |
370 | 2.82k | else if (pkalgo == PUBKEY_ALGO_ECDSA) |
371 | 179 | { |
372 | 179 | if (!data[0] || !data[1]) |
373 | 22 | rc = gpg_error (GPG_ERR_BAD_MPI); |
374 | 157 | else |
375 | 157 | rc = gcry_sexp_build (&s_sig, NULL, |
376 | 157 | "(sig-val(ecdsa(r%m)(s%m)))", data[0], data[1]); |
377 | 179 | } |
378 | 2.64k | else if (pkalgo == PUBKEY_ALGO_EDDSA) |
379 | 1.96k | { |
380 | 1.96k | gcry_mpi_t r = data[0]; |
381 | 1.96k | gcry_mpi_t s = data[1]; |
382 | | |
383 | 1.96k | if (openpgp_oid_is_ed25519 (pkey[0])) |
384 | 1.96k | { |
385 | 1.96k | size_t rlen, slen, n; /* (bytes) */ |
386 | 1.96k | char buf[64]; |
387 | 1.96k | unsigned int nbits; |
388 | 1.96k | unsigned int neededfixedlen = 256 / 8; |
389 | | |
390 | 1.96k | log_assert (neededfixedlen <= sizeof buf); |
391 | | |
392 | 1.96k | if (!r || !s) |
393 | 43 | rc = gpg_error (GPG_ERR_BAD_MPI); |
394 | 1.92k | else if ((rlen = (gcry_mpi_get_nbits (r)+7)/8) > neededfixedlen || !rlen) |
395 | 12 | rc = gpg_error (GPG_ERR_BAD_MPI); |
396 | 1.91k | else if ((slen = (gcry_mpi_get_nbits (s)+7)/8) > neededfixedlen || !slen) |
397 | 43 | rc = gpg_error (GPG_ERR_BAD_MPI); |
398 | 1.86k | else |
399 | 1.86k | { |
400 | 1.86k | r = gcry_mpi_copy (r); |
401 | 1.86k | s = gcry_mpi_copy (s); |
402 | | |
403 | 1.86k | if (!r || !s) |
404 | 0 | { |
405 | 0 | rc = gpg_error_from_syserror (); |
406 | 0 | goto leave; |
407 | 0 | } |
408 | | |
409 | | /* We need to fixup the length in case of leading zeroes. |
410 | | * OpenPGP does not allow leading zeroes and the parser for |
411 | | * the signature packet has no information on the used curve, |
412 | | * thus we need to do it here. We won't do it for opaque |
413 | | * MPIs under the assumption that they are known to be fine; |
414 | | * we won't see them here anyway but the check is anyway |
415 | | * required. Fixme: A nifty feature for gcry_sexp_build |
416 | | * would be a format to left pad the value (e.g. "%*M"). */ |
417 | 1.86k | rc = 0; |
418 | | |
419 | 1.86k | if (rlen < neededfixedlen |
420 | 543 | && !gcry_mpi_get_flag (r, GCRYMPI_FLAG_OPAQUE) |
421 | 199 | && !(rc=gcry_mpi_print (GCRYMPI_FMT_USG, |
422 | 199 | buf, sizeof buf, &n, r))) |
423 | 199 | { |
424 | 199 | log_assert (n < neededfixedlen); |
425 | 199 | memmove (buf + (neededfixedlen - n), buf, n); |
426 | 199 | memset (buf, 0, neededfixedlen - n); |
427 | 199 | gcry_mpi_set_opaque_copy (r, buf, neededfixedlen * 8); |
428 | 199 | } |
429 | 1.66k | else if (rlen < neededfixedlen |
430 | 344 | && gcry_mpi_get_flag (r, GCRYMPI_FLAG_OPAQUE)) |
431 | 344 | { |
432 | 344 | const unsigned char *p; |
433 | | |
434 | 344 | p = gcry_mpi_get_opaque (r, &nbits); |
435 | 344 | n = (nbits+7)/8; |
436 | 344 | memcpy (buf + (neededfixedlen - n), p, n); |
437 | 344 | memset (buf, 0, neededfixedlen - n); |
438 | 344 | gcry_mpi_set_opaque_copy (r, buf, neededfixedlen * 8); |
439 | 344 | } |
440 | | |
441 | 1.86k | if (rc) |
442 | 0 | ; |
443 | 1.86k | else if (slen < neededfixedlen |
444 | 543 | && !gcry_mpi_get_flag (s, GCRYMPI_FLAG_OPAQUE) |
445 | 199 | && !(rc=gcry_mpi_print (GCRYMPI_FMT_USG, |
446 | 199 | buf, sizeof buf, &n, s))) |
447 | 199 | { |
448 | 199 | log_assert (n < neededfixedlen); |
449 | 199 | memmove (buf + (neededfixedlen - n), buf, n); |
450 | 199 | memset (buf, 0, neededfixedlen - n); |
451 | 199 | gcry_mpi_set_opaque_copy (s, buf, neededfixedlen * 8); |
452 | 199 | } |
453 | 1.66k | else if (slen < neededfixedlen |
454 | 344 | && gcry_mpi_get_flag (s, GCRYMPI_FLAG_OPAQUE)) |
455 | 344 | { |
456 | 344 | const unsigned char *p; |
457 | | |
458 | 344 | p = gcry_mpi_get_opaque (s, &nbits); |
459 | 344 | n = (nbits+7)/8; |
460 | 344 | memcpy (buf + (neededfixedlen - n), p, n); |
461 | 344 | memset (buf, 0, neededfixedlen - n); |
462 | 344 | gcry_mpi_set_opaque_copy (s, buf, neededfixedlen * 8); |
463 | 344 | } |
464 | 1.86k | } |
465 | 1.96k | } |
466 | 0 | else |
467 | 0 | rc = 0; |
468 | | |
469 | 1.96k | if (!rc) |
470 | 1.86k | rc = gcry_sexp_build (&s_sig, NULL, |
471 | 1.86k | "(sig-val(eddsa(r%M)(s%M)))", r, s); |
472 | | |
473 | 1.96k | if (r != data[0]) |
474 | 1.86k | gcry_mpi_release (r); |
475 | 1.96k | if (s != data[1]) |
476 | 1.86k | gcry_mpi_release (s); |
477 | 1.96k | } |
478 | 683 | else if (pkalgo == PUBKEY_ALGO_ED25519) |
479 | 0 | { |
480 | 0 | const unsigned char *p; |
481 | 0 | unsigned int nbits; |
482 | |
|
483 | 0 | if (!gcry_mpi_get_flag (data[0], GCRYMPI_FLAG_OPAQUE)) |
484 | 0 | rc = gpg_error (GPG_ERR_BAD_MPI); |
485 | 0 | else |
486 | 0 | { |
487 | 0 | p = gcry_mpi_get_opaque (data[0], &nbits); |
488 | 0 | if ((nbits+7)/8 != 64) |
489 | 0 | rc = gpg_error (GPG_ERR_BAD_MPI); |
490 | 0 | else |
491 | 0 | rc = gcry_sexp_build (&s_sig, NULL, "(sig-val(eddsa(r%b)(s%b)))", |
492 | 0 | 32, p, 32, p+32); |
493 | 0 | } |
494 | 0 | } |
495 | 683 | else if (pkalgo == PUBKEY_ALGO_ELGAMAL || pkalgo == PUBKEY_ALGO_ELGAMAL_E) |
496 | 304 | { |
497 | 304 | if (!data[0] || !data[1]) |
498 | 10 | rc = gpg_error (GPG_ERR_BAD_MPI); |
499 | 294 | else |
500 | 294 | rc = gcry_sexp_build (&s_sig, NULL, |
501 | 294 | "(sig-val(elg(r%m)(s%m)))", data[0], data[1]); |
502 | 304 | } |
503 | 379 | else if (pkalgo == PUBKEY_ALGO_RSA || pkalgo == PUBKEY_ALGO_RSA_S) |
504 | 379 | { |
505 | 379 | if (!data[0]) |
506 | 0 | rc = gpg_error (GPG_ERR_BAD_MPI); |
507 | 379 | else |
508 | 379 | rc = gcry_sexp_build (&s_sig, NULL, "(sig-val(rsa(s%m)))", data[0]); |
509 | 379 | } |
510 | 0 | else |
511 | 0 | BUG (); |
512 | | |
513 | 3.21k | if (!rc) |
514 | 3.01k | rc = gcry_pk_verify (s_sig, s_hash, s_pkey); |
515 | | |
516 | 3.21k | leave: |
517 | 3.21k | gcry_sexp_release (s_sig); |
518 | 3.21k | gcry_sexp_release (s_hash); |
519 | 3.21k | gcry_sexp_release (s_pkey); |
520 | 3.21k | return rc; |
521 | 3.21k | } |
522 | | |
523 | | |
524 | | #if GCRY_KEM_MLKEM1024_ENCAPS_LEN < GCRY_KEM_MLKEM768_ENCAPS_LEN \ |
525 | | || GCRY_KEM_MLKEM1024_SHARED_LEN < GCRY_KEM_MLKEM768_SHARED_LEN |
526 | | # error Bad Kyber constants in Libgcrypt |
527 | | #endif |
528 | | |
529 | | /* Core of the encryption for KEM algorithms. See pk_decrypt for a |
530 | | * description of the arguments. */ |
531 | | static gpg_error_t |
532 | | do_encrypt_kem (PKT_public_key *pk, gcry_mpi_t data, int seskey_algo, |
533 | | gcry_mpi_t *resarr) |
534 | 0 | { |
535 | 0 | gpg_error_t err; |
536 | 0 | int i; |
537 | 0 | unsigned int nbits, n; |
538 | 0 | gcry_sexp_t s_data = NULL; |
539 | 0 | gcry_cipher_hd_t hd = NULL; |
540 | 0 | char *ecc_oid = NULL; |
541 | 0 | const char *curve; |
542 | 0 | const struct gnupg_ecc_params *ecc; |
543 | 0 | enum gcry_kem_algos kyber_algo; |
544 | 0 | int is_rfc9980, only_ecc; |
545 | |
|
546 | 0 | const unsigned char *ecc_pubkey; |
547 | 0 | size_t ecc_pubkey_len; |
548 | 0 | const unsigned char *kyber_pubkey; |
549 | 0 | size_t kyber_pubkey_len; |
550 | 0 | const unsigned char *seskey; |
551 | 0 | size_t seskey_len; |
552 | 0 | unsigned char *enc_seskey = NULL; |
553 | 0 | size_t enc_seskey_len; |
554 | 0 | int ecc_hash_algo; |
555 | |
|
556 | 0 | unsigned char ecc_ct[ECC_POINT_LEN_MAX]; |
557 | 0 | unsigned char ecc_ecdh[ECC_POINT_LEN_MAX]; |
558 | 0 | unsigned char ecc_ss[ECC_HASH_LEN_MAX]; |
559 | 0 | size_t ecc_ct_len, ecc_ecdh_len, ecc_ss_len; |
560 | |
|
561 | 0 | unsigned char kyber_ct[GCRY_KEM_MLKEM1024_ENCAPS_LEN]; |
562 | 0 | unsigned char kyber_ss[GCRY_KEM_MLKEM1024_SHARED_LEN]; |
563 | 0 | size_t kyber_ct_len, kyber_ss_len; |
564 | |
|
565 | 0 | unsigned char kek[32]; /* AES-256 is mandatory. */ |
566 | 0 | size_t kek_len = 32; |
567 | | |
568 | | /* For later error checking we make sure the array is cleared. */ |
569 | 0 | resarr[0] = resarr[1] = resarr[2] = NULL; |
570 | |
|
571 | 0 | only_ecc = 0; |
572 | 0 | if (pk->pubkey_algo == PUBKEY_ALGO_X25519) |
573 | 0 | is_rfc9980 = only_ecc = 1; |
574 | 0 | else if (IS_PUBKEY_ALGO_MLK (pk->pubkey_algo)) |
575 | 0 | is_rfc9980 = 1; |
576 | 0 | else |
577 | 0 | is_rfc9980 = 0; |
578 | | |
579 | | /* As of now we use KEM only for the combined Kyber and thus a |
580 | | * second public key is expected. Right now we take the keys |
581 | | * directly from the PK->data elements. */ |
582 | |
|
583 | 0 | if (is_rfc9980) |
584 | 0 | { |
585 | | /* Note: We need t use the canonical names here. */ |
586 | 0 | switch (pk->pubkey_algo) |
587 | 0 | { |
588 | 0 | case PUBKEY_ALGO_X25519: |
589 | 0 | case PUBKEY_ALGO_MLK768_25519: curve = "ietf25"; break; |
590 | 0 | case PUBKEY_ALGO_MLK768_NP384: curve = "NIST P-384"; break; |
591 | 0 | case PUBKEY_ALGO_MLK768_BP384: curve = "brainpoolP384r1"; break; |
592 | 0 | case PUBKEY_ALGO_MLK1024_448: curve = "X448"; break; |
593 | 0 | case PUBKEY_ALGO_MLK1024_NP521: curve = "NIST P-521"; break; |
594 | 0 | case PUBKEY_ALGO_MLK1024_BP512: curve = "brainpoolP512r1"; break; |
595 | 0 | default: curve = "e_no_such_curve"; break; |
596 | 0 | } |
597 | 0 | } |
598 | 0 | else |
599 | 0 | { |
600 | 0 | ecc_oid = openpgp_oid_to_str (pk->pkey[0]); |
601 | 0 | if (!ecc_oid) |
602 | 0 | { |
603 | 0 | err = gpg_error_from_syserror (); |
604 | 0 | log_error ("%s: error getting OID for ECC key\n", __func__); |
605 | 0 | goto leave; |
606 | 0 | } |
607 | 0 | curve = openpgp_oid_to_curve (ecc_oid, 1); |
608 | 0 | if (!curve) |
609 | 0 | { |
610 | 0 | err = gpg_error (GPG_ERR_INV_DATA); |
611 | 0 | log_error ("%s: error getting curve for ECC key\n", __func__); |
612 | 0 | goto leave; |
613 | 0 | } |
614 | 0 | } |
615 | 0 | ecc = gnupg_get_ecc_params (curve); |
616 | 0 | if (!ecc) |
617 | 0 | { |
618 | 0 | if (opt.verbose) |
619 | 0 | log_info ("%s: ECC curve %s not supported\n", __func__, curve); |
620 | 0 | err = gpg_error (GPG_ERR_INV_DATA); |
621 | 0 | goto leave; |
622 | 0 | } |
623 | 0 | ecc_ct_len = ecc_ecdh_len = ecc->point_len; |
624 | 0 | ecc_hash_algo = ecc->hash_algo; |
625 | 0 | ecc_ss_len = gcry_md_get_algo_dlen (ecc_hash_algo); |
626 | |
|
627 | 0 | ecc_pubkey = gcry_mpi_get_opaque (pk->pkey[is_rfc9980?0:1], &nbits); |
628 | 0 | ecc_pubkey_len = (nbits+7)/8; |
629 | 0 | if (ecc_pubkey_len != ecc->pubkey_len) |
630 | 0 | { |
631 | 0 | if (ecc->kem_algo == GCRY_KEM_RAW_X25519 |
632 | 0 | && ecc_pubkey_len == ecc->pubkey_len - 1) |
633 | 0 | { |
634 | | /* For Curve25519, we also accept no prefix in the point |
635 | | * representation. */ |
636 | 0 | } |
637 | 0 | else |
638 | 0 | { |
639 | 0 | if (opt.verbose) |
640 | 0 | log_info ("%s: ECC public key length invalid (%zu)\n", |
641 | 0 | __func__, ecc_pubkey_len); |
642 | 0 | err = gpg_error (GPG_ERR_INV_DATA); |
643 | 0 | goto leave; |
644 | 0 | } |
645 | 0 | } |
646 | | |
647 | 0 | if (ecc->kem_algo == GCRY_KEM_RAW_X25519) |
648 | 0 | { |
649 | 0 | if (ecc_oid && !strcmp (ecc_oid, "1.3.6.1.4.1.3029.1.5.1")) |
650 | 0 | log_info ("Warning: " |
651 | 0 | "legacy OID for cv25519 accepted during development\n"); |
652 | | /* Optional prefix handling */ |
653 | 0 | if (ecc_pubkey_len == 33 && *ecc_pubkey == 0x40) |
654 | 0 | { |
655 | 0 | ecc_pubkey++; /* Remove the 0x40 prefix. */ |
656 | 0 | ecc_pubkey_len--; |
657 | 0 | } |
658 | 0 | } |
659 | |
|
660 | 0 | if (DBG_CRYPTO) |
661 | 0 | { |
662 | 0 | log_debug ("KEM algo: %d\n", ecc->kem_algo); |
663 | 0 | log_debug ("ECC curve: %s\n", ecc_oid? ecc_oid:curve); |
664 | 0 | log_printhex (ecc_pubkey, ecc_pubkey_len, "ECC pubkey:"); |
665 | 0 | } |
666 | |
|
667 | 0 | err = gcry_kem_encap (ecc->kem_algo, |
668 | 0 | ecc_pubkey, ecc_pubkey_len, |
669 | 0 | ecc_ct, ecc_ct_len, |
670 | 0 | ecc_ecdh, ecc_ecdh_len, |
671 | 0 | NULL, 0); |
672 | 0 | if (err) |
673 | 0 | { |
674 | 0 | if (opt.verbose) |
675 | 0 | log_info ("%s: gcry_kem_encap for ECC (%s) failed\n", |
676 | 0 | __func__, ecc_oid? ecc_oid:curve); |
677 | 0 | goto leave; |
678 | 0 | } |
679 | 0 | if (DBG_CRYPTO) |
680 | 0 | { |
681 | 0 | log_printhex (ecc_ct, ecc_ct_len, "ECC ephem:"); |
682 | 0 | log_printhex (ecc_ecdh, ecc_ecdh_len, "ECC ecdh:"); |
683 | 0 | } |
684 | 0 | if (!is_rfc9980) |
685 | 0 | { |
686 | 0 | err = gnupg_ecc_kem_simple_kdf (ecc_ss, ecc_ss_len, |
687 | 0 | ecc_hash_algo, |
688 | 0 | ecc_ecdh, ecc_ecdh_len, |
689 | 0 | ecc_ct, ecc_ct_len, |
690 | 0 | ecc_pubkey, ecc_pubkey_len); |
691 | 0 | if (err) |
692 | 0 | { |
693 | 0 | if (opt.verbose) |
694 | 0 | log_info ("%s: kdf for ECC failed\n", __func__); |
695 | 0 | goto leave; |
696 | 0 | } |
697 | 0 | if (DBG_CRYPTO) |
698 | 0 | log_printhex (ecc_ss, ecc_ss_len, "ECC shared:"); |
699 | 0 | } |
700 | | |
701 | 0 | if (only_ecc) |
702 | 0 | { |
703 | 0 | kek_len = gcry_cipher_get_algo_keylen (seskey_algo); |
704 | 0 | if (ecc_ct_len != 32 || ecc_pubkey_len != 32 || ecc_ecdh_len != 32 |
705 | 0 | || kek_len > sizeof kek) |
706 | 0 | err = gpg_error (GPG_ERR_INV_LENGTH); |
707 | 0 | else |
708 | 0 | { |
709 | 0 | gcry_kdf_hd_t kdfhd; |
710 | 0 | const char *kdfinfo; |
711 | 0 | unsigned long kdfparam[1]; |
712 | 0 | unsigned char inputbuf[32+32+32]; |
713 | |
|
714 | 0 | kdfparam[0] = kek_len; |
715 | 0 | kdfinfo = "OpenPGP X25519"; |
716 | 0 | memcpy (inputbuf, ecc_ct, 32); |
717 | 0 | memcpy (inputbuf+32, ecc_pubkey, 32); |
718 | 0 | memcpy (inputbuf+64, ecc_ecdh, 32); |
719 | 0 | err = gcry_kdf_open (&kdfhd, GCRY_KDF_HKDF, GCRY_MAC_HMAC_SHA256, |
720 | 0 | kdfparam, 1, |
721 | 0 | inputbuf, sizeof inputbuf, |
722 | 0 | NULL, 0, NULL, 0, |
723 | 0 | kdfinfo, strlen (kdfinfo)); |
724 | 0 | if (!err) |
725 | 0 | { |
726 | 0 | err = gcry_kdf_compute (kdfhd, NULL); |
727 | 0 | if (!err) |
728 | 0 | err = gcry_kdf_final (kdfhd, kek_len, kek); |
729 | 0 | gcry_kdf_close (kdfhd); |
730 | 0 | } |
731 | 0 | wipememory (inputbuf, sizeof inputbuf); |
732 | 0 | } |
733 | 0 | } |
734 | 0 | else /* !only_ecc */ |
735 | 0 | { |
736 | 0 | kyber_pubkey = gcry_mpi_get_opaque (pk->pkey[is_rfc9980?1:2], &nbits); |
737 | 0 | kyber_pubkey_len = (nbits+7)/8; |
738 | 0 | if (kyber_pubkey_len == GCRY_KEM_MLKEM768_PUBKEY_LEN) |
739 | 0 | { |
740 | 0 | kyber_algo = GCRY_KEM_MLKEM768; |
741 | 0 | kyber_ct_len = GCRY_KEM_MLKEM768_ENCAPS_LEN; |
742 | 0 | kyber_ss_len = GCRY_KEM_MLKEM768_SHARED_LEN; |
743 | 0 | } |
744 | 0 | else if (kyber_pubkey_len == GCRY_KEM_MLKEM1024_PUBKEY_LEN) |
745 | 0 | { |
746 | 0 | kyber_algo = GCRY_KEM_MLKEM1024; |
747 | 0 | kyber_ct_len = GCRY_KEM_MLKEM1024_ENCAPS_LEN; |
748 | 0 | kyber_ss_len = GCRY_KEM_MLKEM1024_SHARED_LEN; |
749 | 0 | } |
750 | 0 | else |
751 | 0 | { |
752 | 0 | if (opt.verbose) |
753 | 0 | log_info ("%s: Kyber public key length invalid (%zu)\n", |
754 | 0 | __func__, kyber_pubkey_len); |
755 | 0 | err = gpg_error (GPG_ERR_INV_DATA); |
756 | 0 | goto leave; |
757 | 0 | } |
758 | 0 | if (DBG_CRYPTO) |
759 | 0 | log_printhex (kyber_pubkey, kyber_pubkey_len, "|!trunc|Kyber pubkey:"); |
760 | |
|
761 | 0 | err = gcry_kem_encap (kyber_algo, |
762 | 0 | kyber_pubkey, kyber_pubkey_len, |
763 | 0 | kyber_ct, kyber_ct_len, |
764 | 0 | kyber_ss, kyber_ss_len, |
765 | 0 | NULL, 0); |
766 | 0 | if (err) |
767 | 0 | { |
768 | 0 | if (opt.verbose) |
769 | 0 | log_info ("%s: gcry_kem_encap for ECC failed\n", __func__); |
770 | 0 | goto leave; |
771 | 0 | } |
772 | | |
773 | 0 | if (DBG_CRYPTO) |
774 | 0 | { |
775 | 0 | log_printhex (kyber_ct, kyber_ct_len, "|!trunc|Kyber ephem:"); |
776 | 0 | log_printhex (kyber_ss, kyber_ss_len, "Kyber shared:"); |
777 | 0 | } |
778 | |
|
779 | 0 | if (is_rfc9980) |
780 | 0 | { |
781 | 0 | char fixedinfo[1+22]; /* algid || domSep || len(domSep) */ |
782 | |
|
783 | 0 | fixedinfo[0] = pk->pubkey_algo; |
784 | 0 | memcpy (fixedinfo+1, "OpenPGPCompositeKDFv1\x15", 22); |
785 | |
|
786 | 0 | err = gnupg_kem_combiner_sha3_256 (kek, kek_len, |
787 | 0 | ecc_ecdh, ecc_ct_len, |
788 | 0 | ecc_ct, ecc_ct_len, |
789 | 0 | ecc_pubkey, ecc_pubkey_len, |
790 | 0 | kyber_ss, kyber_ss_len, |
791 | 0 | fixedinfo, sizeof fixedinfo); |
792 | 0 | } |
793 | 0 | else |
794 | 0 | { |
795 | 0 | char fixedinfo[1+MAX_FINGERPRINT_LEN]; |
796 | 0 | int fixedlen; |
797 | |
|
798 | 0 | fixedinfo[0] = seskey_algo; |
799 | 0 | v5_fingerprint_from_pk (pk, fixedinfo+1, NULL); |
800 | 0 | fixedlen = 33; |
801 | |
|
802 | 0 | err = gnupg_kem_combiner (kek, kek_len, |
803 | 0 | ecc_ss, ecc_ss_len, |
804 | 0 | ecc_ct, ecc_ct_len, |
805 | 0 | kyber_ss, kyber_ss_len, |
806 | 0 | kyber_ct, kyber_ct_len, |
807 | 0 | fixedinfo, fixedlen); |
808 | 0 | } |
809 | |
|
810 | 0 | if (err) |
811 | 0 | { |
812 | 0 | if (opt.verbose) |
813 | 0 | log_info ("%s: KEM combiner failed\n", __func__); |
814 | 0 | goto leave; |
815 | 0 | } |
816 | 0 | } /* !only_ecc */ |
817 | | |
818 | 0 | if (DBG_CRYPTO) |
819 | 0 | log_printhex (kek, kek_len, "KEK:"); |
820 | |
|
821 | 0 | err = gcry_cipher_open (&hd, GCRY_CIPHER_AES256, |
822 | 0 | GCRY_CIPHER_MODE_AESWRAP, 0); |
823 | 0 | if (!err) |
824 | 0 | err = gcry_cipher_setkey (hd, kek, kek_len); |
825 | 0 | if (err) |
826 | 0 | { |
827 | 0 | if (opt.verbose) |
828 | 0 | log_error ("%s: failed to initialize AESWRAP: %s\n", __func__, |
829 | 0 | gpg_strerror (err)); |
830 | 0 | goto leave; |
831 | 0 | } |
832 | | |
833 | 0 | err = gcry_sexp_build (&s_data, NULL, "%m", data); |
834 | 0 | if (err) |
835 | 0 | goto leave; |
836 | | |
837 | 0 | n = gcry_cipher_get_algo_keylen (seskey_algo); |
838 | 0 | seskey = gcry_mpi_get_opaque (data, &nbits); |
839 | 0 | seskey_len = (nbits+7)/8; |
840 | 0 | if (seskey_len != n) |
841 | 0 | { |
842 | 0 | if (opt.verbose) |
843 | 0 | log_info ("%s: session key length %zu" |
844 | 0 | " does not match the length for algo %d\n", |
845 | 0 | __func__, seskey_len, seskey_algo); |
846 | 0 | err = gpg_error (GPG_ERR_INV_DATA); |
847 | 0 | goto leave; |
848 | 0 | } |
849 | 0 | if (DBG_CRYPTO) |
850 | 0 | log_printhex (seskey, seskey_len, "seskey:"); |
851 | |
|
852 | 0 | if (is_rfc9980) |
853 | 0 | { |
854 | 0 | enc_seskey_len = seskey_len + 8; |
855 | 0 | enc_seskey = xtrymalloc (enc_seskey_len); |
856 | 0 | if (!enc_seskey) |
857 | 0 | { |
858 | 0 | err = gpg_error_from_syserror (); |
859 | 0 | goto leave; |
860 | 0 | } |
861 | | |
862 | 0 | err = gcry_cipher_encrypt (hd, enc_seskey, enc_seskey_len, |
863 | 0 | seskey, seskey_len); |
864 | 0 | } |
865 | 0 | else |
866 | 0 | { |
867 | 0 | enc_seskey_len = 1 + seskey_len + 8; |
868 | 0 | enc_seskey = xtrymalloc (enc_seskey_len); |
869 | 0 | if (!enc_seskey || enc_seskey_len > 254) |
870 | 0 | { |
871 | 0 | err = gpg_error_from_syserror (); |
872 | 0 | goto leave; |
873 | 0 | } |
874 | | |
875 | 0 | enc_seskey[0] = enc_seskey_len - 1; |
876 | 0 | err = gcry_cipher_encrypt (hd, enc_seskey+1, enc_seskey_len-1, |
877 | 0 | seskey, seskey_len); |
878 | 0 | } |
879 | 0 | if (err) |
880 | 0 | { |
881 | 0 | log_error ("%s: wrapping session key failed\n", __func__); |
882 | 0 | goto leave; |
883 | 0 | } |
884 | 0 | if (DBG_CRYPTO) |
885 | 0 | log_printhex (enc_seskey, enc_seskey_len, "enc_seskey:"); |
886 | |
|
887 | 0 | i= 0; |
888 | 0 | resarr[i++] = gcry_mpi_set_opaque_copy (NULL, ecc_ct, 8 * ecc_ct_len); |
889 | 0 | if (!only_ecc) |
890 | 0 | resarr[i++] = gcry_mpi_set_opaque_copy (NULL, kyber_ct, 8 * kyber_ct_len); |
891 | 0 | resarr[i++] = gcry_mpi_set_opaque_copy (NULL, enc_seskey, 8 * enc_seskey_len); |
892 | |
|
893 | 0 | if (!resarr[0] || !resarr[1] || !(resarr[2] || only_ecc)) |
894 | 0 | { |
895 | 0 | err = gpg_error_from_syserror (); |
896 | 0 | for (i=0; i < 3; i++) |
897 | 0 | gcry_mpi_release (resarr[i]), resarr[i] = NULL; |
898 | 0 | } |
899 | |
|
900 | 0 | leave: |
901 | 0 | wipememory (ecc_ct, sizeof ecc_ct); |
902 | 0 | wipememory (ecc_ecdh, sizeof ecc_ecdh); |
903 | 0 | wipememory (ecc_ss, sizeof ecc_ss); |
904 | 0 | wipememory (kyber_ct, sizeof kyber_ct); |
905 | 0 | wipememory (kyber_ss, sizeof kyber_ss); |
906 | 0 | wipememory (kek, kek_len); |
907 | 0 | xfree (enc_seskey); |
908 | 0 | gcry_cipher_close (hd); |
909 | 0 | xfree (ecc_oid); |
910 | 0 | return err; |
911 | 0 | } |
912 | | |
913 | | |
914 | | /* Core of the encryption for the ECDH algorithms. See pk_decrypt for |
915 | | * a description of the arguments. */ |
916 | | static gpg_error_t |
917 | | do_encrypt_ecdh (PKT_public_key *pk, gcry_mpi_t data, gcry_mpi_t *resarr) |
918 | 0 | { |
919 | 0 | gpg_error_t err; |
920 | 0 | unsigned int nbits; |
921 | 0 | gcry_cipher_hd_t hd = NULL; |
922 | 0 | char *ecc_oid = NULL; |
923 | 0 | const char *curve; |
924 | 0 | const struct gnupg_ecc_params *ecc; |
925 | |
|
926 | 0 | const unsigned char *ecc_pubkey; |
927 | 0 | size_t ecc_pubkey_len; |
928 | 0 | const unsigned char *seskey; |
929 | 0 | size_t seskey_len; |
930 | 0 | unsigned char *enc_seskey = NULL; |
931 | 0 | size_t enc_seskey_len; |
932 | |
|
933 | 0 | unsigned char ecc_ct[ECC_POINT_LEN_MAX]; |
934 | 0 | unsigned char ecc_ecdh[ECC_POINT_LEN_MAX]; |
935 | 0 | size_t ecc_ct_len, ecc_ecdh_len; |
936 | 0 | const char *shared_secret; |
937 | 0 | size_t shared_secretlen; |
938 | 0 | const char *ephemeral_pubkey; |
939 | 0 | size_t ephemeral_pubkeylen; |
940 | |
|
941 | 0 | unsigned char *kek = NULL; |
942 | 0 | size_t kek_len; |
943 | |
|
944 | 0 | const unsigned char *kdf_params_spec; |
945 | 0 | byte fp[MAX_FINGERPRINT_LEN]; |
946 | 0 | int keywrap_cipher_algo; |
947 | 0 | int kdf_hash_algo; |
948 | 0 | unsigned char *kdf_params = NULL; |
949 | 0 | size_t kdf_params_len = 0; |
950 | |
|
951 | 0 | fingerprint_from_pk (pk, fp, NULL); |
952 | |
|
953 | 0 | ecc_oid = openpgp_oid_to_str (pk->pkey[0]); |
954 | 0 | if (!ecc_oid) |
955 | 0 | { |
956 | 0 | err = gpg_error_from_syserror (); |
957 | 0 | log_error ("%s: error getting OID for ECC key\n", __func__); |
958 | 0 | goto leave; |
959 | 0 | } |
960 | 0 | curve = openpgp_oid_to_curve (ecc_oid, 1); |
961 | 0 | if (!curve) |
962 | 0 | { |
963 | 0 | err = gpg_error (GPG_ERR_INV_DATA); |
964 | 0 | log_error ("%s: error getting curve for ECC key\n", __func__); |
965 | 0 | goto leave; |
966 | 0 | } |
967 | 0 | ecc = gnupg_get_ecc_params (curve); |
968 | 0 | if (!ecc) |
969 | 0 | { |
970 | 0 | if (opt.verbose) |
971 | 0 | log_info ("%s: ECC curve %s not supported\n", __func__, curve); |
972 | 0 | err = gpg_error (GPG_ERR_INV_DATA); |
973 | 0 | goto leave; |
974 | 0 | } |
975 | 0 | ecc_ct_len = ecc_ecdh_len = ecc->point_len; |
976 | |
|
977 | 0 | ecc_pubkey = gcry_mpi_get_opaque (pk->pkey[1], &nbits); |
978 | 0 | ecc_pubkey_len = (nbits+7)/8; |
979 | 0 | if (ecc_pubkey_len != ecc->pubkey_len) |
980 | 0 | { |
981 | 0 | if (ecc->kem_algo == GCRY_KEM_RAW_X25519 |
982 | 0 | && ecc_pubkey_len == ecc->pubkey_len - 1) |
983 | | /* For Curve25519, we also accept no prefix in the point |
984 | | * representation. */ |
985 | 0 | ; |
986 | 0 | else |
987 | 0 | { |
988 | 0 | if (opt.verbose) |
989 | 0 | log_info ("%s: ECC public key length invalid (%zu)\n", |
990 | 0 | __func__, ecc_pubkey_len); |
991 | 0 | err = gpg_error (GPG_ERR_INV_DATA); |
992 | 0 | goto leave; |
993 | 0 | } |
994 | 0 | } |
995 | | |
996 | 0 | if (ecc->kem_algo == GCRY_KEM_RAW_X25519) |
997 | 0 | { |
998 | | /* Note: Legacy OID is OK here. */ |
999 | | /* Optional prefix handling */ |
1000 | 0 | if (ecc_pubkey_len == 33 && *ecc_pubkey == 0x40) |
1001 | 0 | { |
1002 | 0 | ecc_pubkey++; /* Remove the 0x40 prefix. */ |
1003 | 0 | ecc_pubkey_len--; |
1004 | 0 | } |
1005 | 0 | } |
1006 | |
|
1007 | 0 | if (DBG_CRYPTO) |
1008 | 0 | { |
1009 | 0 | log_debug ("ECC curve: %s\n", ecc_oid); |
1010 | 0 | log_printhex (ecc_pubkey, ecc_pubkey_len, "ECC pubkey:"); |
1011 | 0 | } |
1012 | |
|
1013 | 0 | err = gcry_kem_encap (ecc->kem_algo, |
1014 | 0 | ecc_pubkey, ecc_pubkey_len, |
1015 | 0 | ecc_ct, ecc_ct_len, |
1016 | 0 | ecc_ecdh, ecc_ecdh_len, |
1017 | 0 | NULL, 0); |
1018 | 0 | if (err) |
1019 | 0 | { |
1020 | 0 | if (opt.verbose) |
1021 | 0 | log_info ("%s: gcry_kem_encap for ECC (%s) failed\n", |
1022 | 0 | __func__, ecc_oid); |
1023 | 0 | goto leave; |
1024 | 0 | } |
1025 | 0 | if (DBG_CRYPTO) |
1026 | 0 | { |
1027 | 0 | log_printhex (ecc_ct, ecc_ct_len, "ECC ephem:"); |
1028 | 0 | log_printhex (ecc_ecdh, ecc_ecdh_len, "ECC ecdh:"); |
1029 | 0 | } |
1030 | |
|
1031 | 0 | if (ecc->is_weierstrauss) |
1032 | 0 | { |
1033 | 0 | shared_secret = ecc_ecdh + 1; |
1034 | 0 | shared_secretlen = (ecc_ecdh_len - 1) / 2; |
1035 | 0 | ephemeral_pubkey = ecc_ct; |
1036 | 0 | ephemeral_pubkeylen = ecc_ct_len; |
1037 | 0 | } |
1038 | 0 | else |
1039 | 0 | { |
1040 | 0 | shared_secret = ecc_ecdh; |
1041 | 0 | shared_secretlen = ecc_ecdh_len; |
1042 | |
|
1043 | 0 | if (ecc->may_have_prefix) |
1044 | 0 | { |
1045 | 0 | ephemeral_pubkeylen = ecc_ct_len + 1; |
1046 | 0 | memmove (ecc_ct + 1, ecc_ct, ecc_ct_len); |
1047 | 0 | ecc_ct[0] = 0x40; |
1048 | 0 | } |
1049 | 0 | else |
1050 | 0 | ephemeral_pubkeylen = ecc_ct_len; |
1051 | |
|
1052 | 0 | ephemeral_pubkey = ecc_ct; |
1053 | 0 | } |
1054 | |
|
1055 | 0 | err = ecc_build_kdf_params (&kdf_params, &kdf_params_len, |
1056 | 0 | &kdf_params_spec, pk->pkey, fp); |
1057 | 0 | if (err) |
1058 | 0 | return err; |
1059 | | |
1060 | 0 | keywrap_cipher_algo = kdf_params_spec[3]; |
1061 | 0 | kdf_hash_algo = kdf_params_spec[2]; |
1062 | |
|
1063 | 0 | if (DBG_CRYPTO) |
1064 | 0 | log_debug ("ecdh KDF algorithms %s+%s with aeswrap\n", |
1065 | 0 | openpgp_md_algo_name (kdf_hash_algo), |
1066 | 0 | openpgp_cipher_algo_name (keywrap_cipher_algo)); |
1067 | |
|
1068 | 0 | if (kdf_hash_algo != GCRY_MD_SHA256 |
1069 | 0 | && kdf_hash_algo != GCRY_MD_SHA384 |
1070 | 0 | && kdf_hash_algo != GCRY_MD_SHA512) |
1071 | 0 | { |
1072 | 0 | err = gpg_error (GPG_ERR_BAD_PUBKEY); |
1073 | 0 | goto leave; |
1074 | 0 | } |
1075 | | |
1076 | 0 | if (keywrap_cipher_algo != CIPHER_ALGO_AES |
1077 | 0 | && keywrap_cipher_algo != CIPHER_ALGO_AES192 |
1078 | 0 | && keywrap_cipher_algo != CIPHER_ALGO_AES256) |
1079 | 0 | { |
1080 | 0 | err = gpg_error (GPG_ERR_BAD_PUBKEY); |
1081 | 0 | goto leave; |
1082 | 0 | } |
1083 | | |
1084 | 0 | kek_len = gcry_cipher_get_algo_keylen (keywrap_cipher_algo); |
1085 | 0 | if (kek_len > gcry_md_get_algo_dlen (kdf_hash_algo)) |
1086 | 0 | { |
1087 | 0 | err = gpg_error (GPG_ERR_BAD_PUBKEY); |
1088 | 0 | goto leave; |
1089 | 0 | } |
1090 | | |
1091 | 0 | kek = xtrymalloc (kek_len); |
1092 | 0 | if (!kek) |
1093 | 0 | { |
1094 | 0 | err = gpg_error_from_syserror (); |
1095 | 0 | goto leave; |
1096 | 0 | } |
1097 | | |
1098 | 0 | err = gnupg_ecc_kem_kdf (kek, kek_len, GCRY_KDF_ONESTEP_KDF, kdf_hash_algo, |
1099 | 0 | shared_secret, shared_secretlen, |
1100 | 0 | kdf_params, kdf_params_len); |
1101 | 0 | xfree (kdf_params); |
1102 | 0 | if (err) |
1103 | 0 | { |
1104 | 0 | if (opt.verbose) |
1105 | 0 | log_info ("%s: kdf for ECC failed\n", __func__); |
1106 | 0 | goto leave; |
1107 | 0 | } |
1108 | | |
1109 | 0 | if (DBG_CRYPTO) |
1110 | 0 | log_printhex (kek, kek_len, "KEK:"); |
1111 | |
|
1112 | 0 | err = gcry_cipher_open (&hd, keywrap_cipher_algo, |
1113 | 0 | GCRY_CIPHER_MODE_AESWRAP, 0); |
1114 | 0 | if (!err) |
1115 | 0 | err = gcry_cipher_setkey (hd, kek, kek_len); |
1116 | 0 | if (err) |
1117 | 0 | { |
1118 | 0 | if (opt.verbose) |
1119 | 0 | log_error ("%s: failed to initialize AESWRAP: %s\n", __func__, |
1120 | 0 | gpg_strerror (err)); |
1121 | 0 | goto leave; |
1122 | 0 | } |
1123 | | |
1124 | 0 | seskey = gcry_mpi_get_opaque (data, &nbits); |
1125 | 0 | seskey_len = (nbits+7)/8; |
1126 | |
|
1127 | 0 | enc_seskey_len = 1 + seskey_len + 8; |
1128 | 0 | enc_seskey = xtrymalloc (enc_seskey_len); |
1129 | 0 | if (!enc_seskey || enc_seskey_len > 254) |
1130 | 0 | { |
1131 | 0 | err = gpg_error_from_syserror (); |
1132 | 0 | goto leave; |
1133 | 0 | } |
1134 | | |
1135 | 0 | enc_seskey[0] = enc_seskey_len - 1; |
1136 | 0 | err = gcry_cipher_encrypt (hd, enc_seskey+1, enc_seskey_len-1, |
1137 | 0 | seskey, seskey_len); |
1138 | 0 | if (err) |
1139 | 0 | { |
1140 | 0 | log_error ("%s: wrapping session key failed\n", __func__); |
1141 | 0 | goto leave; |
1142 | 0 | } |
1143 | 0 | if (DBG_CRYPTO) |
1144 | 0 | log_printhex (enc_seskey, enc_seskey_len, "enc_seskey:"); |
1145 | |
|
1146 | 0 | resarr[0] = gcry_mpi_set_opaque_copy (NULL, ephemeral_pubkey, |
1147 | 0 | 8 * ephemeral_pubkeylen); |
1148 | 0 | if (!resarr[0]) |
1149 | 0 | { |
1150 | 0 | err = gpg_error_from_syserror (); |
1151 | 0 | goto leave; |
1152 | 0 | } |
1153 | | |
1154 | 0 | resarr[1] = gcry_mpi_set_opaque_copy (NULL, enc_seskey, 8 * enc_seskey_len); |
1155 | 0 | if (!resarr[1]) |
1156 | 0 | { |
1157 | 0 | err = gpg_error_from_syserror (); |
1158 | 0 | gcry_mpi_release (resarr[0]); |
1159 | 0 | } |
1160 | |
|
1161 | 0 | leave: |
1162 | 0 | xfree (enc_seskey); |
1163 | 0 | gcry_cipher_close (hd); |
1164 | 0 | xfree (kek); |
1165 | 0 | wipememory (ecc_ct, sizeof ecc_ct); |
1166 | 0 | wipememory (ecc_ecdh, sizeof ecc_ecdh); |
1167 | 0 | xfree (ecc_oid); |
1168 | 0 | return err; |
1169 | 0 | } |
1170 | | |
1171 | | |
1172 | | /* Core of the encryption for RSA and Elgamal algorithms. See |
1173 | | * pk_decrypt for a description of the arguments. */ |
1174 | | static gpg_error_t |
1175 | | do_encrypt_rsa_elg (PKT_public_key *pk, gcry_mpi_t data, gcry_mpi_t *resarr) |
1176 | 0 | { |
1177 | 0 | pubkey_algo_t algo = pk->pubkey_algo; |
1178 | 0 | gcry_mpi_t *pkey = pk->pkey; |
1179 | 0 | gcry_sexp_t s_ciph = NULL; |
1180 | 0 | gcry_sexp_t s_data = NULL; |
1181 | 0 | gcry_sexp_t s_pkey = NULL; |
1182 | 0 | gpg_error_t err; |
1183 | |
|
1184 | 0 | if (algo == PUBKEY_ALGO_ELGAMAL || algo == PUBKEY_ALGO_ELGAMAL_E) |
1185 | 0 | err = gcry_sexp_build (&s_pkey, NULL, |
1186 | 0 | "(public-key(elg(p%m)(g%m)(y%m)))", |
1187 | 0 | pkey[0], pkey[1], pkey[2]); |
1188 | 0 | else |
1189 | 0 | err = gcry_sexp_build (&s_pkey, NULL, |
1190 | 0 | "(public-key(rsa(n%m)(e%m)))", |
1191 | 0 | pkey[0], pkey[1]); |
1192 | 0 | if (err) |
1193 | 0 | goto leave; |
1194 | | |
1195 | 0 | err = gcry_sexp_build (&s_data, NULL, "%m", data); |
1196 | 0 | if (err) |
1197 | 0 | goto leave; |
1198 | | |
1199 | 0 | err = gcry_pk_encrypt (&s_ciph, s_data, s_pkey); |
1200 | 0 | if (err) |
1201 | 0 | goto leave; |
1202 | | |
1203 | 0 | gcry_sexp_release (s_data); s_data = NULL; |
1204 | 0 | gcry_sexp_release (s_pkey); s_pkey = NULL; |
1205 | |
|
1206 | 0 | resarr[0] = get_mpi_from_sexp (s_ciph, "a", GCRYMPI_FMT_USG); |
1207 | 0 | if (!is_RSA (algo)) |
1208 | 0 | resarr[1] = get_mpi_from_sexp (s_ciph, "b", GCRYMPI_FMT_USG); |
1209 | |
|
1210 | 0 | leave: |
1211 | 0 | gcry_sexp_release (s_data); |
1212 | 0 | gcry_sexp_release (s_pkey); |
1213 | 0 | gcry_sexp_release (s_ciph); |
1214 | 0 | return err; |
1215 | 0 | } |
1216 | | |
1217 | | |
1218 | | /* |
1219 | | * Emulate our old PK interface here - sometime in the future we might |
1220 | | * change the internal design to directly fit to libgcrypt. PK is is |
1221 | | * the OpenPGP public key packet, DATA is an MPI with the to be |
1222 | | * encrypted data, and RESARR receives the encrypted data. RESARRAY |
1223 | | * is expected to be an two/three item array which will be filled with |
1224 | | * newly allocated MPIs. SESKEY_ALGO is required for public key |
1225 | | * algorithms which do not encode it in DATA. |
1226 | | */ |
1227 | | gpg_error_t |
1228 | | pk_encrypt (PKT_public_key *pk, gcry_mpi_t data, int seskey_algo, |
1229 | | gcry_mpi_t *resarr) |
1230 | 0 | { |
1231 | 0 | pubkey_algo_t algo = pk->pubkey_algo; |
1232 | |
|
1233 | 0 | if (algo == PUBKEY_ALGO_KYBER) |
1234 | 0 | return do_encrypt_kem (pk, data, seskey_algo, resarr); |
1235 | 0 | else if (algo == PUBKEY_ALGO_ECDH) |
1236 | 0 | return do_encrypt_ecdh (pk, data, resarr); |
1237 | 0 | else if (algo == PUBKEY_ALGO_ELGAMAL || algo == PUBKEY_ALGO_ELGAMAL_E) |
1238 | 0 | return do_encrypt_rsa_elg (pk, data, resarr); |
1239 | 0 | else if (algo == PUBKEY_ALGO_RSA || algo == PUBKEY_ALGO_RSA_E) |
1240 | 0 | return do_encrypt_rsa_elg (pk, data, resarr); |
1241 | 0 | else if (RFC9980 && (algo == PUBKEY_ALGO_X25519 |
1242 | 0 | || IS_PUBKEY_ALGO_MLK (algo))) |
1243 | 0 | return do_encrypt_kem (pk, data, seskey_algo, resarr); |
1244 | 0 | else |
1245 | 0 | return gpg_error (GPG_ERR_PUBKEY_ALGO); |
1246 | 0 | } |
1247 | | |
1248 | | |
1249 | | /* Check whether SKEY is a suitable secret key. */ |
1250 | | int |
1251 | | pk_check_secret_key (pubkey_algo_t pkalgo, gcry_mpi_t *skey) |
1252 | 0 | { |
1253 | 0 | gcry_sexp_t s_skey; |
1254 | 0 | int rc; |
1255 | |
|
1256 | 0 | if (pkalgo == PUBKEY_ALGO_DSA) |
1257 | 0 | { |
1258 | 0 | rc = gcry_sexp_build (&s_skey, NULL, |
1259 | 0 | "(private-key(dsa(p%m)(q%m)(g%m)(y%m)(x%m)))", |
1260 | 0 | skey[0], skey[1], skey[2], skey[3], skey[4]); |
1261 | 0 | } |
1262 | 0 | else if (pkalgo == PUBKEY_ALGO_ELGAMAL || pkalgo == PUBKEY_ALGO_ELGAMAL_E) |
1263 | 0 | { |
1264 | 0 | rc = gcry_sexp_build (&s_skey, NULL, |
1265 | 0 | "(private-key(elg(p%m)(g%m)(y%m)(x%m)))", |
1266 | 0 | skey[0], skey[1], skey[2], skey[3]); |
1267 | 0 | } |
1268 | 0 | else if (is_RSA (pkalgo)) |
1269 | 0 | { |
1270 | 0 | rc = gcry_sexp_build (&s_skey, NULL, |
1271 | 0 | "(private-key(rsa(n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))", |
1272 | 0 | skey[0], skey[1], skey[2], skey[3], skey[4], |
1273 | 0 | skey[5]); |
1274 | 0 | } |
1275 | 0 | else if (pkalgo == PUBKEY_ALGO_ECDSA || pkalgo == PUBKEY_ALGO_ECDH) |
1276 | 0 | { |
1277 | 0 | char *curve = openpgp_oid_to_str (skey[0]); |
1278 | 0 | if (!curve) |
1279 | 0 | rc = gpg_error_from_syserror (); |
1280 | 0 | else |
1281 | 0 | { |
1282 | 0 | rc = gcry_sexp_build (&s_skey, NULL, |
1283 | 0 | "(private-key(ecc(curve%s)(q%m)(d%m)))", |
1284 | 0 | curve, skey[1], skey[2]); |
1285 | 0 | xfree (curve); |
1286 | 0 | } |
1287 | 0 | } |
1288 | 0 | else if (pkalgo == PUBKEY_ALGO_EDDSA) |
1289 | 0 | { |
1290 | 0 | char *curve = openpgp_oid_to_str (skey[0]); |
1291 | 0 | if (!curve) |
1292 | 0 | rc = gpg_error_from_syserror (); |
1293 | 0 | else |
1294 | 0 | { |
1295 | 0 | const char *fmt; |
1296 | |
|
1297 | 0 | if (openpgp_oid_is_ed25519 (skey[0])) |
1298 | 0 | fmt = "(private-key(ecc(curve %s)(flags eddsa)(q%m)(d%m)))"; |
1299 | 0 | else |
1300 | 0 | fmt = "(private-key(ecc(curve %s)(q%m)(d%m)))"; |
1301 | |
|
1302 | 0 | rc = gcry_sexp_build (&s_skey, NULL, fmt, curve, skey[1], skey[2]); |
1303 | 0 | xfree (curve); |
1304 | 0 | } |
1305 | 0 | } |
1306 | 0 | else |
1307 | 0 | return GPG_ERR_PUBKEY_ALGO; |
1308 | | |
1309 | 0 | if (!rc) |
1310 | 0 | { |
1311 | 0 | rc = gcry_pk_testkey (s_skey); |
1312 | 0 | gcry_sexp_release (s_skey); |
1313 | 0 | } |
1314 | 0 | return rc; |
1315 | 0 | } |