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.67k | { |
269 | 3.67k | gcry_sexp_t s_sig, s_hash, s_pkey; |
270 | 3.67k | int rc; |
271 | | |
272 | | /* Make a sexp from pkey. */ |
273 | 3.67k | if (pkalgo == PUBKEY_ALGO_DSA) |
274 | 421 | { |
275 | 421 | rc = gcry_sexp_build (&s_pkey, NULL, |
276 | 421 | "(public-key(dsa(p%m)(q%m)(g%m)(y%m)))", |
277 | 421 | pkey[0], pkey[1], pkey[2], pkey[3]); |
278 | 421 | } |
279 | 3.24k | else if (pkalgo == PUBKEY_ALGO_ELGAMAL_E || pkalgo == PUBKEY_ALGO_ELGAMAL) |
280 | 525 | { |
281 | 525 | rc = gcry_sexp_build (&s_pkey, NULL, |
282 | 525 | "(public-key(elg(p%m)(g%m)(y%m)))", |
283 | 525 | pkey[0], pkey[1], pkey[2]); |
284 | 525 | } |
285 | 2.72k | else if (pkalgo == PUBKEY_ALGO_RSA || pkalgo == PUBKEY_ALGO_RSA_S) |
286 | 788 | { |
287 | 788 | rc = gcry_sexp_build (&s_pkey, NULL, |
288 | 788 | "(public-key(rsa(n%m)(e%m)))", pkey[0], pkey[1]); |
289 | 788 | } |
290 | 1.93k | else if (pkalgo == PUBKEY_ALGO_ECDSA) |
291 | 168 | { |
292 | 168 | char *curve = openpgp_oid_to_str (pkey[0]); |
293 | 168 | if (!curve) |
294 | 0 | rc = gpg_error_from_syserror (); |
295 | 168 | else |
296 | 168 | { |
297 | 168 | rc = gcry_sexp_build (&s_pkey, NULL, |
298 | 168 | "(public-key(ecdsa(curve %s)(q%m)))", |
299 | 168 | curve, pkey[1]); |
300 | 168 | xfree (curve); |
301 | 168 | } |
302 | 168 | } |
303 | 1.76k | else if (pkalgo == PUBKEY_ALGO_EDDSA) |
304 | 1.67k | { |
305 | 1.67k | char *curve = openpgp_oid_to_str (pkey[0]); |
306 | 1.67k | if (!curve) |
307 | 0 | rc = gpg_error_from_syserror (); |
308 | 1.67k | else |
309 | 1.67k | { |
310 | 1.67k | const char *fmt; |
311 | | |
312 | 1.67k | if (openpgp_oid_is_ed25519 (pkey[0])) |
313 | 1.67k | 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.67k | rc = gcry_sexp_build (&s_pkey, NULL, fmt, curve, pkey[1]); |
318 | 1.67k | xfree (curve); |
319 | 1.67k | } |
320 | 1.67k | } |
321 | 92 | 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 | 92 | else |
329 | 92 | return GPG_ERR_PUBKEY_ALGO; |
330 | | |
331 | 3.57k | if (rc) |
332 | 0 | BUG (); /* gcry_sexp_build should never fail. */ |
333 | | |
334 | | /* Put hash into a S-Exp s_hash. */ |
335 | 3.57k | if (pkalgo == PUBKEY_ALGO_EDDSA) |
336 | 1.67k | { |
337 | 1.67k | const char *fmt; |
338 | | |
339 | 1.67k | if (openpgp_oid_is_ed25519 (pkey[0])) |
340 | 1.67k | fmt = "(data(flags eddsa)(hash-algo sha512)(value %m))"; |
341 | 0 | else |
342 | 0 | fmt = "(data(value %m))"; |
343 | | |
344 | 1.67k | if (gcry_sexp_build (&s_hash, NULL, fmt, hash)) |
345 | 0 | BUG (); /* gcry_sexp_build should never fail. */ |
346 | 1.67k | } |
347 | 1.90k | 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.90k | else |
355 | 1.90k | { |
356 | 1.90k | if (gcry_sexp_build (&s_hash, NULL, "%m", hash)) |
357 | 0 | BUG (); /* gcry_sexp_build should never fail. */ |
358 | 1.90k | } |
359 | | |
360 | | /* Put data into a S-Exp s_sig. */ |
361 | 3.57k | s_sig = NULL; |
362 | 3.57k | if (pkalgo == PUBKEY_ALGO_DSA) |
363 | 421 | { |
364 | 421 | if (!data[0] || !data[1]) |
365 | 93 | rc = gpg_error (GPG_ERR_BAD_MPI); |
366 | 328 | else |
367 | 328 | rc = gcry_sexp_build (&s_sig, NULL, |
368 | 328 | "(sig-val(dsa(r%m)(s%m)))", data[0], data[1]); |
369 | 421 | } |
370 | 3.15k | else if (pkalgo == PUBKEY_ALGO_ECDSA) |
371 | 168 | { |
372 | 168 | if (!data[0] || !data[1]) |
373 | 24 | rc = gpg_error (GPG_ERR_BAD_MPI); |
374 | 144 | else |
375 | 144 | rc = gcry_sexp_build (&s_sig, NULL, |
376 | 144 | "(sig-val(ecdsa(r%m)(s%m)))", data[0], data[1]); |
377 | 168 | } |
378 | 2.98k | else if (pkalgo == PUBKEY_ALGO_EDDSA) |
379 | 1.67k | { |
380 | 1.67k | gcry_mpi_t r = data[0]; |
381 | 1.67k | gcry_mpi_t s = data[1]; |
382 | | |
383 | 1.67k | if (openpgp_oid_is_ed25519 (pkey[0])) |
384 | 1.67k | { |
385 | 1.67k | size_t rlen, slen, n; /* (bytes) */ |
386 | 1.67k | char buf[64]; |
387 | 1.67k | unsigned int nbits; |
388 | 1.67k | unsigned int neededfixedlen = 256 / 8; |
389 | | |
390 | 1.67k | log_assert (neededfixedlen <= sizeof buf); |
391 | | |
392 | 1.67k | if (!r || !s) |
393 | 44 | rc = gpg_error (GPG_ERR_BAD_MPI); |
394 | 1.63k | else if ((rlen = (gcry_mpi_get_nbits (r)+7)/8) > neededfixedlen || !rlen) |
395 | 11 | rc = gpg_error (GPG_ERR_BAD_MPI); |
396 | 1.62k | else if ((slen = (gcry_mpi_get_nbits (s)+7)/8) > neededfixedlen || !slen) |
397 | 48 | rc = gpg_error (GPG_ERR_BAD_MPI); |
398 | 1.57k | else |
399 | 1.57k | { |
400 | 1.57k | r = gcry_mpi_copy (r); |
401 | 1.57k | s = gcry_mpi_copy (s); |
402 | | |
403 | 1.57k | 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.57k | rc = 0; |
418 | | |
419 | 1.57k | if (rlen < neededfixedlen |
420 | 435 | && !gcry_mpi_get_flag (r, GCRYMPI_FLAG_OPAQUE) |
421 | 183 | && !(rc=gcry_mpi_print (GCRYMPI_FMT_USG, |
422 | 183 | buf, sizeof buf, &n, r))) |
423 | 183 | { |
424 | 183 | log_assert (n < neededfixedlen); |
425 | 183 | memmove (buf + (neededfixedlen - n), buf, n); |
426 | 183 | memset (buf, 0, neededfixedlen - n); |
427 | 183 | gcry_mpi_set_opaque_copy (r, buf, neededfixedlen * 8); |
428 | 183 | } |
429 | 1.39k | else if (rlen < neededfixedlen |
430 | 252 | && gcry_mpi_get_flag (r, GCRYMPI_FLAG_OPAQUE)) |
431 | 252 | { |
432 | 252 | const unsigned char *p; |
433 | | |
434 | 252 | p = gcry_mpi_get_opaque (r, &nbits); |
435 | 252 | n = (nbits+7)/8; |
436 | 252 | memcpy (buf + (neededfixedlen - n), p, n); |
437 | 252 | memset (buf, 0, neededfixedlen - n); |
438 | 252 | gcry_mpi_set_opaque_copy (r, buf, neededfixedlen * 8); |
439 | 252 | } |
440 | | |
441 | 1.57k | if (rc) |
442 | 0 | ; |
443 | 1.57k | else if (slen < neededfixedlen |
444 | 435 | && !gcry_mpi_get_flag (s, GCRYMPI_FLAG_OPAQUE) |
445 | 183 | && !(rc=gcry_mpi_print (GCRYMPI_FMT_USG, |
446 | 183 | buf, sizeof buf, &n, s))) |
447 | 183 | { |
448 | 183 | log_assert (n < neededfixedlen); |
449 | 183 | memmove (buf + (neededfixedlen - n), buf, n); |
450 | 183 | memset (buf, 0, neededfixedlen - n); |
451 | 183 | gcry_mpi_set_opaque_copy (s, buf, neededfixedlen * 8); |
452 | 183 | } |
453 | 1.39k | else if (slen < neededfixedlen |
454 | 252 | && gcry_mpi_get_flag (s, GCRYMPI_FLAG_OPAQUE)) |
455 | 252 | { |
456 | 252 | const unsigned char *p; |
457 | | |
458 | 252 | p = gcry_mpi_get_opaque (s, &nbits); |
459 | 252 | n = (nbits+7)/8; |
460 | 252 | memcpy (buf + (neededfixedlen - n), p, n); |
461 | 252 | memset (buf, 0, neededfixedlen - n); |
462 | 252 | gcry_mpi_set_opaque_copy (s, buf, neededfixedlen * 8); |
463 | 252 | } |
464 | 1.57k | } |
465 | 1.67k | } |
466 | 0 | else |
467 | 0 | rc = 0; |
468 | | |
469 | 1.67k | if (!rc) |
470 | 1.57k | rc = gcry_sexp_build (&s_sig, NULL, |
471 | 1.57k | "(sig-val(eddsa(r%M)(s%M)))", r, s); |
472 | | |
473 | 1.67k | if (r != data[0]) |
474 | 1.57k | gcry_mpi_release (r); |
475 | 1.67k | if (s != data[1]) |
476 | 1.57k | gcry_mpi_release (s); |
477 | 1.67k | } |
478 | 1.31k | 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 | 1.31k | else if (pkalgo == PUBKEY_ALGO_ELGAMAL || pkalgo == PUBKEY_ALGO_ELGAMAL_E) |
496 | 525 | { |
497 | 525 | if (!data[0] || !data[1]) |
498 | 23 | rc = gpg_error (GPG_ERR_BAD_MPI); |
499 | 502 | else |
500 | 502 | rc = gcry_sexp_build (&s_sig, NULL, |
501 | 502 | "(sig-val(elg(r%m)(s%m)))", data[0], data[1]); |
502 | 525 | } |
503 | 788 | else if (pkalgo == PUBKEY_ALGO_RSA || pkalgo == PUBKEY_ALGO_RSA_S) |
504 | 788 | { |
505 | 788 | if (!data[0]) |
506 | 0 | rc = gpg_error (GPG_ERR_BAD_MPI); |
507 | 788 | else |
508 | 788 | rc = gcry_sexp_build (&s_sig, NULL, "(sig-val(rsa(s%m)))", data[0]); |
509 | 788 | } |
510 | 0 | else |
511 | 0 | BUG (); |
512 | | |
513 | 3.57k | if (!rc) |
514 | 3.33k | rc = gcry_pk_verify (s_sig, s_hash, s_pkey); |
515 | | |
516 | 3.57k | leave: |
517 | 3.57k | gcry_sexp_release (s_sig); |
518 | 3.57k | gcry_sexp_release (s_hash); |
519 | 3.57k | gcry_sexp_release (s_pkey); |
520 | 3.57k | return rc; |
521 | 3.57k | } |
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 | kyber_ct_len = 0; /* Avoid false compiler warning. Its use is |
734 | | * actually controlled by the only_ecc |
735 | | * condition. */ |
736 | 0 | } |
737 | 0 | else /* !only_ecc */ |
738 | 0 | { |
739 | 0 | kyber_pubkey = gcry_mpi_get_opaque (pk->pkey[is_rfc9980?1:2], &nbits); |
740 | 0 | kyber_pubkey_len = (nbits+7)/8; |
741 | 0 | if (kyber_pubkey_len == GCRY_KEM_MLKEM768_PUBKEY_LEN) |
742 | 0 | { |
743 | 0 | kyber_algo = GCRY_KEM_MLKEM768; |
744 | 0 | kyber_ct_len = GCRY_KEM_MLKEM768_ENCAPS_LEN; |
745 | 0 | kyber_ss_len = GCRY_KEM_MLKEM768_SHARED_LEN; |
746 | 0 | } |
747 | 0 | else if (kyber_pubkey_len == GCRY_KEM_MLKEM1024_PUBKEY_LEN) |
748 | 0 | { |
749 | 0 | kyber_algo = GCRY_KEM_MLKEM1024; |
750 | 0 | kyber_ct_len = GCRY_KEM_MLKEM1024_ENCAPS_LEN; |
751 | 0 | kyber_ss_len = GCRY_KEM_MLKEM1024_SHARED_LEN; |
752 | 0 | } |
753 | 0 | else |
754 | 0 | { |
755 | 0 | if (opt.verbose) |
756 | 0 | log_info ("%s: Kyber public key length invalid (%zu)\n", |
757 | 0 | __func__, kyber_pubkey_len); |
758 | 0 | err = gpg_error (GPG_ERR_INV_DATA); |
759 | 0 | goto leave; |
760 | 0 | } |
761 | 0 | if (DBG_CRYPTO) |
762 | 0 | log_printhex (kyber_pubkey, kyber_pubkey_len, "|!trunc|Kyber pubkey:"); |
763 | |
|
764 | 0 | err = gcry_kem_encap (kyber_algo, |
765 | 0 | kyber_pubkey, kyber_pubkey_len, |
766 | 0 | kyber_ct, kyber_ct_len, |
767 | 0 | kyber_ss, kyber_ss_len, |
768 | 0 | NULL, 0); |
769 | 0 | if (err) |
770 | 0 | { |
771 | 0 | if (opt.verbose) |
772 | 0 | log_info ("%s: gcry_kem_encap for ECC failed\n", __func__); |
773 | 0 | goto leave; |
774 | 0 | } |
775 | | |
776 | 0 | if (DBG_CRYPTO) |
777 | 0 | { |
778 | 0 | log_printhex (kyber_ct, kyber_ct_len, "|!trunc|Kyber ephem:"); |
779 | 0 | log_printhex (kyber_ss, kyber_ss_len, "Kyber shared:"); |
780 | 0 | } |
781 | |
|
782 | 0 | if (is_rfc9980) |
783 | 0 | { |
784 | 0 | char fixedinfo[1+22]; /* algid || domSep || len(domSep) */ |
785 | |
|
786 | 0 | fixedinfo[0] = pk->pubkey_algo; |
787 | 0 | memcpy (fixedinfo+1, "OpenPGPCompositeKDFv1\x15", 22); |
788 | |
|
789 | 0 | err = gnupg_kem_combiner_sha3_256 (kek, kek_len, |
790 | 0 | ecc_ecdh, ecc_ct_len, |
791 | 0 | ecc_ct, ecc_ct_len, |
792 | 0 | ecc_pubkey, ecc_pubkey_len, |
793 | 0 | kyber_ss, kyber_ss_len, |
794 | 0 | fixedinfo, sizeof fixedinfo); |
795 | 0 | } |
796 | 0 | else |
797 | 0 | { |
798 | 0 | char fixedinfo[1+MAX_FINGERPRINT_LEN]; |
799 | 0 | int fixedlen; |
800 | |
|
801 | 0 | fixedinfo[0] = seskey_algo; |
802 | 0 | v5_fingerprint_from_pk (pk, fixedinfo+1, NULL); |
803 | 0 | fixedlen = 33; |
804 | |
|
805 | 0 | err = gnupg_kem_combiner (kek, kek_len, |
806 | 0 | ecc_ss, ecc_ss_len, |
807 | 0 | ecc_ct, ecc_ct_len, |
808 | 0 | kyber_ss, kyber_ss_len, |
809 | 0 | kyber_ct, kyber_ct_len, |
810 | 0 | fixedinfo, fixedlen); |
811 | 0 | } |
812 | |
|
813 | 0 | if (err) |
814 | 0 | { |
815 | 0 | if (opt.verbose) |
816 | 0 | log_info ("%s: KEM combiner failed\n", __func__); |
817 | 0 | goto leave; |
818 | 0 | } |
819 | 0 | } /* !only_ecc */ |
820 | | |
821 | 0 | if (DBG_CRYPTO) |
822 | 0 | log_printhex (kek, kek_len, "KEK:"); |
823 | |
|
824 | 0 | err = gcry_cipher_open (&hd, GCRY_CIPHER_AES256, |
825 | 0 | GCRY_CIPHER_MODE_AESWRAP, 0); |
826 | 0 | if (!err) |
827 | 0 | err = gcry_cipher_setkey (hd, kek, kek_len); |
828 | 0 | if (err) |
829 | 0 | { |
830 | 0 | if (opt.verbose) |
831 | 0 | log_error ("%s: failed to initialize AESWRAP: %s\n", __func__, |
832 | 0 | gpg_strerror (err)); |
833 | 0 | goto leave; |
834 | 0 | } |
835 | | |
836 | 0 | err = gcry_sexp_build (&s_data, NULL, "%m", data); |
837 | 0 | if (err) |
838 | 0 | goto leave; |
839 | | |
840 | 0 | n = gcry_cipher_get_algo_keylen (seskey_algo); |
841 | 0 | seskey = gcry_mpi_get_opaque (data, &nbits); |
842 | 0 | seskey_len = (nbits+7)/8; |
843 | 0 | if (seskey_len != n) |
844 | 0 | { |
845 | 0 | if (opt.verbose) |
846 | 0 | log_info ("%s: session key length %zu" |
847 | 0 | " does not match the length for algo %d\n", |
848 | 0 | __func__, seskey_len, seskey_algo); |
849 | 0 | err = gpg_error (GPG_ERR_INV_DATA); |
850 | 0 | goto leave; |
851 | 0 | } |
852 | 0 | if (DBG_CRYPTO) |
853 | 0 | log_printhex (seskey, seskey_len, "seskey:"); |
854 | |
|
855 | 0 | if (is_rfc9980) |
856 | 0 | { |
857 | 0 | enc_seskey_len = seskey_len + 8; |
858 | 0 | enc_seskey = xtrymalloc (enc_seskey_len); |
859 | 0 | if (!enc_seskey) |
860 | 0 | { |
861 | 0 | err = gpg_error_from_syserror (); |
862 | 0 | goto leave; |
863 | 0 | } |
864 | | |
865 | 0 | err = gcry_cipher_encrypt (hd, enc_seskey, enc_seskey_len, |
866 | 0 | seskey, seskey_len); |
867 | 0 | } |
868 | 0 | else |
869 | 0 | { |
870 | 0 | enc_seskey_len = 1 + seskey_len + 8; |
871 | 0 | enc_seskey = xtrymalloc (enc_seskey_len); |
872 | 0 | if (!enc_seskey || enc_seskey_len > 254) |
873 | 0 | { |
874 | 0 | err = gpg_error_from_syserror (); |
875 | 0 | goto leave; |
876 | 0 | } |
877 | | |
878 | 0 | enc_seskey[0] = enc_seskey_len - 1; |
879 | 0 | err = gcry_cipher_encrypt (hd, enc_seskey+1, enc_seskey_len-1, |
880 | 0 | seskey, seskey_len); |
881 | 0 | } |
882 | 0 | if (err) |
883 | 0 | { |
884 | 0 | log_error ("%s: wrapping session key failed\n", __func__); |
885 | 0 | goto leave; |
886 | 0 | } |
887 | 0 | if (DBG_CRYPTO) |
888 | 0 | log_printhex (enc_seskey, enc_seskey_len, "enc_seskey:"); |
889 | |
|
890 | 0 | i= 0; |
891 | 0 | resarr[i++] = gcry_mpi_set_opaque_copy (NULL, ecc_ct, 8 * ecc_ct_len); |
892 | 0 | if (!only_ecc) |
893 | 0 | resarr[i++] = gcry_mpi_set_opaque_copy (NULL, kyber_ct, 8 * kyber_ct_len); |
894 | 0 | resarr[i++] = gcry_mpi_set_opaque_copy (NULL, enc_seskey, 8 * enc_seskey_len); |
895 | |
|
896 | 0 | if (!resarr[0] || !resarr[1] || !(resarr[2] || only_ecc)) |
897 | 0 | { |
898 | 0 | err = gpg_error_from_syserror (); |
899 | 0 | for (i=0; i < 3; i++) |
900 | 0 | gcry_mpi_release (resarr[i]), resarr[i] = NULL; |
901 | 0 | } |
902 | |
|
903 | 0 | leave: |
904 | 0 | wipememory (ecc_ct, sizeof ecc_ct); |
905 | 0 | wipememory (ecc_ecdh, sizeof ecc_ecdh); |
906 | 0 | wipememory (ecc_ss, sizeof ecc_ss); |
907 | 0 | wipememory (kyber_ct, sizeof kyber_ct); |
908 | 0 | wipememory (kyber_ss, sizeof kyber_ss); |
909 | 0 | wipememory (kek, kek_len); |
910 | 0 | xfree (enc_seskey); |
911 | 0 | gcry_cipher_close (hd); |
912 | 0 | xfree (ecc_oid); |
913 | 0 | return err; |
914 | 0 | } |
915 | | |
916 | | |
917 | | /* Core of the encryption for the ECDH algorithms. See pk_decrypt for |
918 | | * a description of the arguments. */ |
919 | | static gpg_error_t |
920 | | do_encrypt_ecdh (PKT_public_key *pk, gcry_mpi_t data, gcry_mpi_t *resarr) |
921 | 0 | { |
922 | 0 | gpg_error_t err; |
923 | 0 | unsigned int nbits; |
924 | 0 | gcry_cipher_hd_t hd = NULL; |
925 | 0 | char *ecc_oid = NULL; |
926 | 0 | const char *curve; |
927 | 0 | const struct gnupg_ecc_params *ecc; |
928 | |
|
929 | 0 | const unsigned char *ecc_pubkey; |
930 | 0 | size_t ecc_pubkey_len; |
931 | 0 | const unsigned char *seskey; |
932 | 0 | size_t seskey_len; |
933 | 0 | unsigned char *enc_seskey = NULL; |
934 | 0 | size_t enc_seskey_len; |
935 | |
|
936 | 0 | unsigned char ecc_ct[ECC_POINT_LEN_MAX]; |
937 | 0 | unsigned char ecc_ecdh[ECC_POINT_LEN_MAX]; |
938 | 0 | size_t ecc_ct_len, ecc_ecdh_len; |
939 | 0 | const char *shared_secret; |
940 | 0 | size_t shared_secretlen; |
941 | 0 | const char *ephemeral_pubkey; |
942 | 0 | size_t ephemeral_pubkeylen; |
943 | |
|
944 | 0 | unsigned char *kek = NULL; |
945 | 0 | size_t kek_len; |
946 | |
|
947 | 0 | const unsigned char *kdf_params_spec; |
948 | 0 | byte fp[MAX_FINGERPRINT_LEN]; |
949 | 0 | int keywrap_cipher_algo; |
950 | 0 | int kdf_hash_algo; |
951 | 0 | unsigned char *kdf_params = NULL; |
952 | 0 | size_t kdf_params_len = 0; |
953 | |
|
954 | 0 | fingerprint_from_pk (pk, fp, NULL); |
955 | |
|
956 | 0 | ecc_oid = openpgp_oid_to_str (pk->pkey[0]); |
957 | 0 | if (!ecc_oid) |
958 | 0 | { |
959 | 0 | err = gpg_error_from_syserror (); |
960 | 0 | log_error ("%s: error getting OID for ECC key\n", __func__); |
961 | 0 | goto leave; |
962 | 0 | } |
963 | 0 | curve = openpgp_oid_to_curve (ecc_oid, 1); |
964 | 0 | if (!curve) |
965 | 0 | { |
966 | 0 | err = gpg_error (GPG_ERR_INV_DATA); |
967 | 0 | log_error ("%s: error getting curve for ECC key\n", __func__); |
968 | 0 | goto leave; |
969 | 0 | } |
970 | 0 | ecc = gnupg_get_ecc_params (curve); |
971 | 0 | if (!ecc) |
972 | 0 | { |
973 | 0 | if (opt.verbose) |
974 | 0 | log_info ("%s: ECC curve %s not supported\n", __func__, curve); |
975 | 0 | err = gpg_error (GPG_ERR_INV_DATA); |
976 | 0 | goto leave; |
977 | 0 | } |
978 | 0 | ecc_ct_len = ecc_ecdh_len = ecc->point_len; |
979 | |
|
980 | 0 | ecc_pubkey = gcry_mpi_get_opaque (pk->pkey[1], &nbits); |
981 | 0 | ecc_pubkey_len = (nbits+7)/8; |
982 | 0 | if (ecc_pubkey_len != ecc->pubkey_len) |
983 | 0 | { |
984 | 0 | if (ecc->kem_algo == GCRY_KEM_RAW_X25519 |
985 | 0 | && ecc_pubkey_len == ecc->pubkey_len - 1) |
986 | | /* For Curve25519, we also accept no prefix in the point |
987 | | * representation. */ |
988 | 0 | ; |
989 | 0 | else |
990 | 0 | { |
991 | 0 | if (opt.verbose) |
992 | 0 | log_info ("%s: ECC public key length invalid (%zu)\n", |
993 | 0 | __func__, ecc_pubkey_len); |
994 | 0 | err = gpg_error (GPG_ERR_INV_DATA); |
995 | 0 | goto leave; |
996 | 0 | } |
997 | 0 | } |
998 | | |
999 | 0 | if (ecc->kem_algo == GCRY_KEM_RAW_X25519) |
1000 | 0 | { |
1001 | | /* Note: Legacy OID is OK here. */ |
1002 | | /* Optional prefix handling */ |
1003 | 0 | if (ecc_pubkey_len == 33 && *ecc_pubkey == 0x40) |
1004 | 0 | { |
1005 | 0 | ecc_pubkey++; /* Remove the 0x40 prefix. */ |
1006 | 0 | ecc_pubkey_len--; |
1007 | 0 | } |
1008 | 0 | } |
1009 | |
|
1010 | 0 | if (DBG_CRYPTO) |
1011 | 0 | { |
1012 | 0 | log_debug ("ECC curve: %s\n", ecc_oid); |
1013 | 0 | log_printhex (ecc_pubkey, ecc_pubkey_len, "ECC pubkey:"); |
1014 | 0 | } |
1015 | |
|
1016 | 0 | err = gcry_kem_encap (ecc->kem_algo, |
1017 | 0 | ecc_pubkey, ecc_pubkey_len, |
1018 | 0 | ecc_ct, ecc_ct_len, |
1019 | 0 | ecc_ecdh, ecc_ecdh_len, |
1020 | 0 | NULL, 0); |
1021 | 0 | if (err) |
1022 | 0 | { |
1023 | 0 | if (opt.verbose) |
1024 | 0 | log_info ("%s: gcry_kem_encap for ECC (%s) failed\n", |
1025 | 0 | __func__, ecc_oid); |
1026 | 0 | goto leave; |
1027 | 0 | } |
1028 | 0 | if (DBG_CRYPTO) |
1029 | 0 | { |
1030 | 0 | log_printhex (ecc_ct, ecc_ct_len, "ECC ephem:"); |
1031 | 0 | log_printhex (ecc_ecdh, ecc_ecdh_len, "ECC ecdh:"); |
1032 | 0 | } |
1033 | |
|
1034 | 0 | if (ecc->is_weierstrauss) |
1035 | 0 | { |
1036 | 0 | shared_secret = ecc_ecdh + 1; |
1037 | 0 | shared_secretlen = (ecc_ecdh_len - 1) / 2; |
1038 | 0 | ephemeral_pubkey = ecc_ct; |
1039 | 0 | ephemeral_pubkeylen = ecc_ct_len; |
1040 | 0 | } |
1041 | 0 | else |
1042 | 0 | { |
1043 | 0 | shared_secret = ecc_ecdh; |
1044 | 0 | shared_secretlen = ecc_ecdh_len; |
1045 | |
|
1046 | 0 | if (ecc->may_have_prefix) |
1047 | 0 | { |
1048 | 0 | ephemeral_pubkeylen = ecc_ct_len + 1; |
1049 | 0 | memmove (ecc_ct + 1, ecc_ct, ecc_ct_len); |
1050 | 0 | ecc_ct[0] = 0x40; |
1051 | 0 | } |
1052 | 0 | else |
1053 | 0 | ephemeral_pubkeylen = ecc_ct_len; |
1054 | |
|
1055 | 0 | ephemeral_pubkey = ecc_ct; |
1056 | 0 | } |
1057 | |
|
1058 | 0 | err = ecc_build_kdf_params (&kdf_params, &kdf_params_len, |
1059 | 0 | &kdf_params_spec, pk->pkey, fp); |
1060 | 0 | if (err) |
1061 | 0 | return err; |
1062 | | |
1063 | 0 | keywrap_cipher_algo = kdf_params_spec[3]; |
1064 | 0 | kdf_hash_algo = kdf_params_spec[2]; |
1065 | |
|
1066 | 0 | if (DBG_CRYPTO) |
1067 | 0 | log_debug ("ecdh KDF algorithms %s+%s with aeswrap\n", |
1068 | 0 | openpgp_md_algo_name (kdf_hash_algo), |
1069 | 0 | openpgp_cipher_algo_name (keywrap_cipher_algo)); |
1070 | |
|
1071 | 0 | if (kdf_hash_algo != GCRY_MD_SHA256 |
1072 | 0 | && kdf_hash_algo != GCRY_MD_SHA384 |
1073 | 0 | && kdf_hash_algo != GCRY_MD_SHA512) |
1074 | 0 | { |
1075 | 0 | err = gpg_error (GPG_ERR_BAD_PUBKEY); |
1076 | 0 | goto leave; |
1077 | 0 | } |
1078 | | |
1079 | 0 | if (keywrap_cipher_algo != CIPHER_ALGO_AES |
1080 | 0 | && keywrap_cipher_algo != CIPHER_ALGO_AES192 |
1081 | 0 | && keywrap_cipher_algo != CIPHER_ALGO_AES256) |
1082 | 0 | { |
1083 | 0 | err = gpg_error (GPG_ERR_BAD_PUBKEY); |
1084 | 0 | goto leave; |
1085 | 0 | } |
1086 | | |
1087 | 0 | kek_len = gcry_cipher_get_algo_keylen (keywrap_cipher_algo); |
1088 | 0 | if (kek_len > gcry_md_get_algo_dlen (kdf_hash_algo)) |
1089 | 0 | { |
1090 | 0 | err = gpg_error (GPG_ERR_BAD_PUBKEY); |
1091 | 0 | goto leave; |
1092 | 0 | } |
1093 | | |
1094 | 0 | kek = xtrymalloc (kek_len); |
1095 | 0 | if (!kek) |
1096 | 0 | { |
1097 | 0 | err = gpg_error_from_syserror (); |
1098 | 0 | goto leave; |
1099 | 0 | } |
1100 | | |
1101 | 0 | err = gnupg_ecc_kem_kdf (kek, kek_len, GCRY_KDF_ONESTEP_KDF, kdf_hash_algo, |
1102 | 0 | shared_secret, shared_secretlen, |
1103 | 0 | kdf_params, kdf_params_len); |
1104 | 0 | xfree (kdf_params); |
1105 | 0 | if (err) |
1106 | 0 | { |
1107 | 0 | if (opt.verbose) |
1108 | 0 | log_info ("%s: kdf for ECC failed\n", __func__); |
1109 | 0 | goto leave; |
1110 | 0 | } |
1111 | | |
1112 | 0 | if (DBG_CRYPTO) |
1113 | 0 | log_printhex (kek, kek_len, "KEK:"); |
1114 | |
|
1115 | 0 | err = gcry_cipher_open (&hd, keywrap_cipher_algo, |
1116 | 0 | GCRY_CIPHER_MODE_AESWRAP, 0); |
1117 | 0 | if (!err) |
1118 | 0 | err = gcry_cipher_setkey (hd, kek, kek_len); |
1119 | 0 | if (err) |
1120 | 0 | { |
1121 | 0 | if (opt.verbose) |
1122 | 0 | log_error ("%s: failed to initialize AESWRAP: %s\n", __func__, |
1123 | 0 | gpg_strerror (err)); |
1124 | 0 | goto leave; |
1125 | 0 | } |
1126 | | |
1127 | 0 | seskey = gcry_mpi_get_opaque (data, &nbits); |
1128 | 0 | seskey_len = (nbits+7)/8; |
1129 | |
|
1130 | 0 | enc_seskey_len = 1 + seskey_len + 8; |
1131 | 0 | enc_seskey = xtrymalloc (enc_seskey_len); |
1132 | 0 | if (!enc_seskey || enc_seskey_len > 254) |
1133 | 0 | { |
1134 | 0 | err = gpg_error_from_syserror (); |
1135 | 0 | goto leave; |
1136 | 0 | } |
1137 | | |
1138 | 0 | enc_seskey[0] = enc_seskey_len - 1; |
1139 | 0 | err = gcry_cipher_encrypt (hd, enc_seskey+1, enc_seskey_len-1, |
1140 | 0 | seskey, seskey_len); |
1141 | 0 | if (err) |
1142 | 0 | { |
1143 | 0 | log_error ("%s: wrapping session key failed\n", __func__); |
1144 | 0 | goto leave; |
1145 | 0 | } |
1146 | 0 | if (DBG_CRYPTO) |
1147 | 0 | log_printhex (enc_seskey, enc_seskey_len, "enc_seskey:"); |
1148 | |
|
1149 | 0 | resarr[0] = gcry_mpi_set_opaque_copy (NULL, ephemeral_pubkey, |
1150 | 0 | 8 * ephemeral_pubkeylen); |
1151 | 0 | if (!resarr[0]) |
1152 | 0 | { |
1153 | 0 | err = gpg_error_from_syserror (); |
1154 | 0 | goto leave; |
1155 | 0 | } |
1156 | | |
1157 | 0 | resarr[1] = gcry_mpi_set_opaque_copy (NULL, enc_seskey, 8 * enc_seskey_len); |
1158 | 0 | if (!resarr[1]) |
1159 | 0 | { |
1160 | 0 | err = gpg_error_from_syserror (); |
1161 | 0 | gcry_mpi_release (resarr[0]); |
1162 | 0 | } |
1163 | |
|
1164 | 0 | leave: |
1165 | 0 | xfree (enc_seskey); |
1166 | 0 | gcry_cipher_close (hd); |
1167 | 0 | xfree (kek); |
1168 | 0 | wipememory (ecc_ct, sizeof ecc_ct); |
1169 | 0 | wipememory (ecc_ecdh, sizeof ecc_ecdh); |
1170 | 0 | xfree (ecc_oid); |
1171 | 0 | return err; |
1172 | 0 | } |
1173 | | |
1174 | | |
1175 | | /* Core of the encryption for RSA and Elgamal algorithms. See |
1176 | | * pk_decrypt for a description of the arguments. */ |
1177 | | static gpg_error_t |
1178 | | do_encrypt_rsa_elg (PKT_public_key *pk, gcry_mpi_t data, gcry_mpi_t *resarr) |
1179 | 0 | { |
1180 | 0 | pubkey_algo_t algo = pk->pubkey_algo; |
1181 | 0 | gcry_mpi_t *pkey = pk->pkey; |
1182 | 0 | gcry_sexp_t s_ciph = NULL; |
1183 | 0 | gcry_sexp_t s_data = NULL; |
1184 | 0 | gcry_sexp_t s_pkey = NULL; |
1185 | 0 | gpg_error_t err; |
1186 | |
|
1187 | 0 | if (algo == PUBKEY_ALGO_ELGAMAL || algo == PUBKEY_ALGO_ELGAMAL_E) |
1188 | 0 | err = gcry_sexp_build (&s_pkey, NULL, |
1189 | 0 | "(public-key(elg(p%m)(g%m)(y%m)))", |
1190 | 0 | pkey[0], pkey[1], pkey[2]); |
1191 | 0 | else |
1192 | 0 | err = gcry_sexp_build (&s_pkey, NULL, |
1193 | 0 | "(public-key(rsa(n%m)(e%m)))", |
1194 | 0 | pkey[0], pkey[1]); |
1195 | 0 | if (err) |
1196 | 0 | goto leave; |
1197 | | |
1198 | 0 | err = gcry_sexp_build (&s_data, NULL, "%m", data); |
1199 | 0 | if (err) |
1200 | 0 | goto leave; |
1201 | | |
1202 | 0 | err = gcry_pk_encrypt (&s_ciph, s_data, s_pkey); |
1203 | 0 | if (err) |
1204 | 0 | goto leave; |
1205 | | |
1206 | 0 | gcry_sexp_release (s_data); s_data = NULL; |
1207 | 0 | gcry_sexp_release (s_pkey); s_pkey = NULL; |
1208 | |
|
1209 | 0 | resarr[0] = get_mpi_from_sexp (s_ciph, "a", GCRYMPI_FMT_USG); |
1210 | 0 | if (!is_RSA (algo)) |
1211 | 0 | resarr[1] = get_mpi_from_sexp (s_ciph, "b", GCRYMPI_FMT_USG); |
1212 | |
|
1213 | 0 | leave: |
1214 | 0 | gcry_sexp_release (s_data); |
1215 | 0 | gcry_sexp_release (s_pkey); |
1216 | 0 | gcry_sexp_release (s_ciph); |
1217 | 0 | return err; |
1218 | 0 | } |
1219 | | |
1220 | | |
1221 | | /* |
1222 | | * Emulate our old PK interface here - sometime in the future we might |
1223 | | * change the internal design to directly fit to libgcrypt. PK is is |
1224 | | * the OpenPGP public key packet, DATA is an MPI with the to be |
1225 | | * encrypted data, and RESARR receives the encrypted data. RESARRAY |
1226 | | * is expected to be an two/three item array which will be filled with |
1227 | | * newly allocated MPIs. SESKEY_ALGO is required for public key |
1228 | | * algorithms which do not encode it in DATA. |
1229 | | */ |
1230 | | gpg_error_t |
1231 | | pk_encrypt (PKT_public_key *pk, gcry_mpi_t data, int seskey_algo, |
1232 | | gcry_mpi_t *resarr) |
1233 | 0 | { |
1234 | 0 | pubkey_algo_t algo = pk->pubkey_algo; |
1235 | |
|
1236 | 0 | if (algo == PUBKEY_ALGO_KYBER) |
1237 | 0 | return do_encrypt_kem (pk, data, seskey_algo, resarr); |
1238 | 0 | else if (algo == PUBKEY_ALGO_ECDH) |
1239 | 0 | return do_encrypt_ecdh (pk, data, resarr); |
1240 | 0 | else if (algo == PUBKEY_ALGO_ELGAMAL || algo == PUBKEY_ALGO_ELGAMAL_E) |
1241 | 0 | return do_encrypt_rsa_elg (pk, data, resarr); |
1242 | 0 | else if (algo == PUBKEY_ALGO_RSA || algo == PUBKEY_ALGO_RSA_E) |
1243 | 0 | return do_encrypt_rsa_elg (pk, data, resarr); |
1244 | 0 | else if (RFC9980 && (algo == PUBKEY_ALGO_X25519 |
1245 | 0 | || IS_PUBKEY_ALGO_MLK (algo))) |
1246 | 0 | return do_encrypt_kem (pk, data, seskey_algo, resarr); |
1247 | 0 | else |
1248 | 0 | return gpg_error (GPG_ERR_PUBKEY_ALGO); |
1249 | 0 | } |
1250 | | |
1251 | | |
1252 | | /* Check whether SKEY is a suitable secret key. */ |
1253 | | int |
1254 | | pk_check_secret_key (pubkey_algo_t pkalgo, gcry_mpi_t *skey) |
1255 | 0 | { |
1256 | 0 | gcry_sexp_t s_skey; |
1257 | 0 | int rc; |
1258 | |
|
1259 | 0 | if (pkalgo == PUBKEY_ALGO_DSA) |
1260 | 0 | { |
1261 | 0 | rc = gcry_sexp_build (&s_skey, NULL, |
1262 | 0 | "(private-key(dsa(p%m)(q%m)(g%m)(y%m)(x%m)))", |
1263 | 0 | skey[0], skey[1], skey[2], skey[3], skey[4]); |
1264 | 0 | } |
1265 | 0 | else if (pkalgo == PUBKEY_ALGO_ELGAMAL || pkalgo == PUBKEY_ALGO_ELGAMAL_E) |
1266 | 0 | { |
1267 | 0 | rc = gcry_sexp_build (&s_skey, NULL, |
1268 | 0 | "(private-key(elg(p%m)(g%m)(y%m)(x%m)))", |
1269 | 0 | skey[0], skey[1], skey[2], skey[3]); |
1270 | 0 | } |
1271 | 0 | else if (is_RSA (pkalgo)) |
1272 | 0 | { |
1273 | 0 | rc = gcry_sexp_build (&s_skey, NULL, |
1274 | 0 | "(private-key(rsa(n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))", |
1275 | 0 | skey[0], skey[1], skey[2], skey[3], skey[4], |
1276 | 0 | skey[5]); |
1277 | 0 | } |
1278 | 0 | else if (pkalgo == PUBKEY_ALGO_ECDSA || pkalgo == PUBKEY_ALGO_ECDH) |
1279 | 0 | { |
1280 | 0 | char *curve = openpgp_oid_to_str (skey[0]); |
1281 | 0 | if (!curve) |
1282 | 0 | rc = gpg_error_from_syserror (); |
1283 | 0 | else |
1284 | 0 | { |
1285 | 0 | rc = gcry_sexp_build (&s_skey, NULL, |
1286 | 0 | "(private-key(ecc(curve%s)(q%m)(d%m)))", |
1287 | 0 | curve, skey[1], skey[2]); |
1288 | 0 | xfree (curve); |
1289 | 0 | } |
1290 | 0 | } |
1291 | 0 | else if (pkalgo == PUBKEY_ALGO_EDDSA) |
1292 | 0 | { |
1293 | 0 | char *curve = openpgp_oid_to_str (skey[0]); |
1294 | 0 | if (!curve) |
1295 | 0 | rc = gpg_error_from_syserror (); |
1296 | 0 | else |
1297 | 0 | { |
1298 | 0 | const char *fmt; |
1299 | |
|
1300 | 0 | if (openpgp_oid_is_ed25519 (skey[0])) |
1301 | 0 | fmt = "(private-key(ecc(curve %s)(flags eddsa)(q%m)(d%m)))"; |
1302 | 0 | else |
1303 | 0 | fmt = "(private-key(ecc(curve %s)(q%m)(d%m)))"; |
1304 | |
|
1305 | 0 | rc = gcry_sexp_build (&s_skey, NULL, fmt, curve, skey[1], skey[2]); |
1306 | 0 | xfree (curve); |
1307 | 0 | } |
1308 | 0 | } |
1309 | 0 | else |
1310 | 0 | return GPG_ERR_PUBKEY_ALGO; |
1311 | | |
1312 | 0 | if (!rc) |
1313 | 0 | { |
1314 | 0 | rc = gcry_pk_testkey (s_skey); |
1315 | 0 | gcry_sexp_release (s_skey); |
1316 | 0 | } |
1317 | 0 | return rc; |
1318 | 0 | } |