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