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