/src/boringssl/crypto/hpke/hpke.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (c) 2020, Google Inc. |
2 | | * |
3 | | * Permission to use, copy, modify, and/or distribute this software for any |
4 | | * purpose with or without fee is hereby granted, provided that the above |
5 | | * copyright notice and this permission notice appear in all copies. |
6 | | * |
7 | | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
8 | | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
9 | | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
10 | | * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
11 | | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
12 | | * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
13 | | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ |
14 | | |
15 | | #include <openssl/hpke.h> |
16 | | |
17 | | #include <assert.h> |
18 | | #include <string.h> |
19 | | |
20 | | #include <openssl/aead.h> |
21 | | #include <openssl/bytestring.h> |
22 | | #include <openssl/curve25519.h> |
23 | | #include <openssl/digest.h> |
24 | | #include <openssl/err.h> |
25 | | #include <openssl/evp_errors.h> |
26 | | #include <openssl/hkdf.h> |
27 | | #include <openssl/rand.h> |
28 | | #include <openssl/sha.h> |
29 | | |
30 | | #include "../internal.h" |
31 | | |
32 | | |
33 | | // This file implements RFC 9180. |
34 | | |
35 | | #define MAX_SEED_LEN X25519_PRIVATE_KEY_LEN |
36 | | #define MAX_SHARED_SECRET_LEN SHA256_DIGEST_LENGTH |
37 | | |
38 | | struct evp_hpke_kem_st { |
39 | | uint16_t id; |
40 | | size_t public_key_len; |
41 | | size_t private_key_len; |
42 | | size_t seed_len; |
43 | | size_t enc_len; |
44 | | int (*init_key)(EVP_HPKE_KEY *key, const uint8_t *priv_key, |
45 | | size_t priv_key_len); |
46 | | int (*generate_key)(EVP_HPKE_KEY *key); |
47 | | int (*encap_with_seed)(const EVP_HPKE_KEM *kem, uint8_t *out_shared_secret, |
48 | | size_t *out_shared_secret_len, uint8_t *out_enc, |
49 | | size_t *out_enc_len, size_t max_enc, |
50 | | const uint8_t *peer_public_key, |
51 | | size_t peer_public_key_len, const uint8_t *seed, |
52 | | size_t seed_len); |
53 | | int (*decap)(const EVP_HPKE_KEY *key, uint8_t *out_shared_secret, |
54 | | size_t *out_shared_secret_len, const uint8_t *enc, |
55 | | size_t enc_len); |
56 | | int (*auth_encap_with_seed)(const EVP_HPKE_KEY *key, |
57 | | uint8_t *out_shared_secret, |
58 | | size_t *out_shared_secret_len, uint8_t *out_enc, |
59 | | size_t *out_enc_len, size_t max_enc, |
60 | | const uint8_t *peer_public_key, |
61 | | size_t peer_public_key_len, const uint8_t *seed, |
62 | | size_t seed_len); |
63 | | int (*auth_decap)(const EVP_HPKE_KEY *key, uint8_t *out_shared_secret, |
64 | | size_t *out_shared_secret_len, const uint8_t *enc, |
65 | | size_t enc_len, const uint8_t *peer_public_key, |
66 | | size_t peer_public_key_len); |
67 | | }; |
68 | | |
69 | | struct evp_hpke_kdf_st { |
70 | | uint16_t id; |
71 | | // We only support HKDF-based KDFs. |
72 | | const EVP_MD *(*hkdf_md_func)(void); |
73 | | }; |
74 | | |
75 | | struct evp_hpke_aead_st { |
76 | | uint16_t id; |
77 | | const EVP_AEAD *(*aead_func)(void); |
78 | | }; |
79 | | |
80 | | |
81 | | // Low-level labeled KDF functions. |
82 | | |
83 | | static const char kHpkeVersionId[] = "HPKE-v1"; |
84 | | |
85 | 0 | static int add_label_string(CBB *cbb, const char *label) { |
86 | 0 | return CBB_add_bytes(cbb, (const uint8_t *)label, strlen(label)); |
87 | 0 | } |
88 | | |
89 | | static int hpke_labeled_extract(const EVP_MD *hkdf_md, uint8_t *out_key, |
90 | | size_t *out_len, const uint8_t *salt, |
91 | | size_t salt_len, const uint8_t *suite_id, |
92 | | size_t suite_id_len, const char *label, |
93 | 0 | const uint8_t *ikm, size_t ikm_len) { |
94 | | // labeledIKM = concat("HPKE-v1", suite_id, label, IKM) |
95 | 0 | CBB labeled_ikm; |
96 | 0 | int ok = CBB_init(&labeled_ikm, 0) && |
97 | 0 | add_label_string(&labeled_ikm, kHpkeVersionId) && |
98 | 0 | CBB_add_bytes(&labeled_ikm, suite_id, suite_id_len) && |
99 | 0 | add_label_string(&labeled_ikm, label) && |
100 | 0 | CBB_add_bytes(&labeled_ikm, ikm, ikm_len) && |
101 | 0 | HKDF_extract(out_key, out_len, hkdf_md, CBB_data(&labeled_ikm), |
102 | 0 | CBB_len(&labeled_ikm), salt, salt_len); |
103 | 0 | CBB_cleanup(&labeled_ikm); |
104 | 0 | return ok; |
105 | 0 | } |
106 | | |
107 | | static int hpke_labeled_expand(const EVP_MD *hkdf_md, uint8_t *out_key, |
108 | | size_t out_len, const uint8_t *prk, |
109 | | size_t prk_len, const uint8_t *suite_id, |
110 | | size_t suite_id_len, const char *label, |
111 | 0 | const uint8_t *info, size_t info_len) { |
112 | | // labeledInfo = concat(I2OSP(L, 2), "HPKE-v1", suite_id, label, info) |
113 | 0 | CBB labeled_info; |
114 | 0 | int ok = CBB_init(&labeled_info, 0) && |
115 | 0 | CBB_add_u16(&labeled_info, out_len) && |
116 | 0 | add_label_string(&labeled_info, kHpkeVersionId) && |
117 | 0 | CBB_add_bytes(&labeled_info, suite_id, suite_id_len) && |
118 | 0 | add_label_string(&labeled_info, label) && |
119 | 0 | CBB_add_bytes(&labeled_info, info, info_len) && |
120 | 0 | HKDF_expand(out_key, out_len, hkdf_md, prk, prk_len, |
121 | 0 | CBB_data(&labeled_info), CBB_len(&labeled_info)); |
122 | 0 | CBB_cleanup(&labeled_info); |
123 | 0 | return ok; |
124 | 0 | } |
125 | | |
126 | | |
127 | | // KEM implementations. |
128 | | |
129 | | // dhkem_extract_and_expand implements the ExtractAndExpand operation in the |
130 | | // DHKEM construction. See section 4.1 of RFC 9180. |
131 | | static int dhkem_extract_and_expand(uint16_t kem_id, const EVP_MD *hkdf_md, |
132 | | uint8_t *out_key, size_t out_len, |
133 | | const uint8_t *dh, size_t dh_len, |
134 | | const uint8_t *kem_context, |
135 | 0 | size_t kem_context_len) { |
136 | | // concat("KEM", I2OSP(kem_id, 2)) |
137 | 0 | uint8_t suite_id[5] = {'K', 'E', 'M', kem_id >> 8, kem_id & 0xff}; |
138 | 0 | uint8_t prk[EVP_MAX_MD_SIZE]; |
139 | 0 | size_t prk_len; |
140 | 0 | return hpke_labeled_extract(hkdf_md, prk, &prk_len, NULL, 0, suite_id, |
141 | 0 | sizeof(suite_id), "eae_prk", dh, dh_len) && |
142 | 0 | hpke_labeled_expand(hkdf_md, out_key, out_len, prk, prk_len, suite_id, |
143 | 0 | sizeof(suite_id), "shared_secret", kem_context, |
144 | 0 | kem_context_len); |
145 | 0 | } |
146 | | |
147 | | static int x25519_init_key(EVP_HPKE_KEY *key, const uint8_t *priv_key, |
148 | 0 | size_t priv_key_len) { |
149 | 0 | if (priv_key_len != X25519_PRIVATE_KEY_LEN) { |
150 | 0 | OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR); |
151 | 0 | return 0; |
152 | 0 | } |
153 | | |
154 | 0 | OPENSSL_memcpy(key->private_key, priv_key, priv_key_len); |
155 | 0 | X25519_public_from_private(key->public_key, priv_key); |
156 | 0 | return 1; |
157 | 0 | } |
158 | | |
159 | 0 | static int x25519_generate_key(EVP_HPKE_KEY *key) { |
160 | 0 | X25519_keypair(key->public_key, key->private_key); |
161 | 0 | return 1; |
162 | 0 | } |
163 | | |
164 | | static int x25519_encap_with_seed( |
165 | | const EVP_HPKE_KEM *kem, uint8_t *out_shared_secret, |
166 | | size_t *out_shared_secret_len, uint8_t *out_enc, size_t *out_enc_len, |
167 | | size_t max_enc, const uint8_t *peer_public_key, size_t peer_public_key_len, |
168 | 0 | const uint8_t *seed, size_t seed_len) { |
169 | 0 | if (max_enc < X25519_PUBLIC_VALUE_LEN) { |
170 | 0 | OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_BUFFER_SIZE); |
171 | 0 | return 0; |
172 | 0 | } |
173 | 0 | if (seed_len != X25519_PRIVATE_KEY_LEN) { |
174 | 0 | OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR); |
175 | 0 | return 0; |
176 | 0 | } |
177 | 0 | X25519_public_from_private(out_enc, seed); |
178 | |
|
179 | 0 | uint8_t dh[X25519_SHARED_KEY_LEN]; |
180 | 0 | if (peer_public_key_len != X25519_PUBLIC_VALUE_LEN || |
181 | 0 | !X25519(dh, seed, peer_public_key)) { |
182 | 0 | OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_PEER_KEY); |
183 | 0 | return 0; |
184 | 0 | } |
185 | | |
186 | 0 | uint8_t kem_context[2 * X25519_PUBLIC_VALUE_LEN]; |
187 | 0 | OPENSSL_memcpy(kem_context, out_enc, X25519_PUBLIC_VALUE_LEN); |
188 | 0 | OPENSSL_memcpy(kem_context + X25519_PUBLIC_VALUE_LEN, peer_public_key, |
189 | 0 | X25519_PUBLIC_VALUE_LEN); |
190 | 0 | if (!dhkem_extract_and_expand(kem->id, EVP_sha256(), out_shared_secret, |
191 | 0 | SHA256_DIGEST_LENGTH, dh, sizeof(dh), |
192 | 0 | kem_context, sizeof(kem_context))) { |
193 | 0 | return 0; |
194 | 0 | } |
195 | | |
196 | 0 | *out_enc_len = X25519_PUBLIC_VALUE_LEN; |
197 | 0 | *out_shared_secret_len = SHA256_DIGEST_LENGTH; |
198 | 0 | return 1; |
199 | 0 | } |
200 | | |
201 | | static int x25519_decap(const EVP_HPKE_KEY *key, uint8_t *out_shared_secret, |
202 | | size_t *out_shared_secret_len, const uint8_t *enc, |
203 | 0 | size_t enc_len) { |
204 | 0 | uint8_t dh[X25519_SHARED_KEY_LEN]; |
205 | 0 | if (enc_len != X25519_PUBLIC_VALUE_LEN || |
206 | 0 | !X25519(dh, key->private_key, enc)) { |
207 | 0 | OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_PEER_KEY); |
208 | 0 | return 0; |
209 | 0 | } |
210 | | |
211 | 0 | uint8_t kem_context[2 * X25519_PUBLIC_VALUE_LEN]; |
212 | 0 | OPENSSL_memcpy(kem_context, enc, X25519_PUBLIC_VALUE_LEN); |
213 | 0 | OPENSSL_memcpy(kem_context + X25519_PUBLIC_VALUE_LEN, key->public_key, |
214 | 0 | X25519_PUBLIC_VALUE_LEN); |
215 | 0 | if (!dhkem_extract_and_expand(key->kem->id, EVP_sha256(), out_shared_secret, |
216 | 0 | SHA256_DIGEST_LENGTH, dh, sizeof(dh), |
217 | 0 | kem_context, sizeof(kem_context))) { |
218 | 0 | return 0; |
219 | 0 | } |
220 | | |
221 | 0 | *out_shared_secret_len = SHA256_DIGEST_LENGTH; |
222 | 0 | return 1; |
223 | 0 | } |
224 | | |
225 | | static int x25519_auth_encap_with_seed( |
226 | | const EVP_HPKE_KEY *key, uint8_t *out_shared_secret, |
227 | | size_t *out_shared_secret_len, uint8_t *out_enc, size_t *out_enc_len, |
228 | | size_t max_enc, const uint8_t *peer_public_key, size_t peer_public_key_len, |
229 | 0 | const uint8_t *seed, size_t seed_len) { |
230 | 0 | if (max_enc < X25519_PUBLIC_VALUE_LEN) { |
231 | 0 | OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_BUFFER_SIZE); |
232 | 0 | return 0; |
233 | 0 | } |
234 | 0 | if (seed_len != X25519_PRIVATE_KEY_LEN) { |
235 | 0 | OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR); |
236 | 0 | return 0; |
237 | 0 | } |
238 | 0 | X25519_public_from_private(out_enc, seed); |
239 | |
|
240 | 0 | uint8_t dh[2 * X25519_SHARED_KEY_LEN]; |
241 | 0 | if (peer_public_key_len != X25519_PUBLIC_VALUE_LEN || |
242 | 0 | !X25519(dh, seed, peer_public_key) || |
243 | 0 | !X25519(dh + X25519_SHARED_KEY_LEN, key->private_key, peer_public_key)) { |
244 | 0 | OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_PEER_KEY); |
245 | 0 | return 0; |
246 | 0 | } |
247 | | |
248 | 0 | uint8_t kem_context[3 * X25519_PUBLIC_VALUE_LEN]; |
249 | 0 | OPENSSL_memcpy(kem_context, out_enc, X25519_PUBLIC_VALUE_LEN); |
250 | 0 | OPENSSL_memcpy(kem_context + X25519_PUBLIC_VALUE_LEN, peer_public_key, |
251 | 0 | X25519_PUBLIC_VALUE_LEN); |
252 | 0 | OPENSSL_memcpy(kem_context + 2 * X25519_PUBLIC_VALUE_LEN, key->public_key, |
253 | 0 | X25519_PUBLIC_VALUE_LEN); |
254 | 0 | if (!dhkem_extract_and_expand(key->kem->id, EVP_sha256(), out_shared_secret, |
255 | 0 | SHA256_DIGEST_LENGTH, dh, sizeof(dh), |
256 | 0 | kem_context, sizeof(kem_context))) { |
257 | 0 | return 0; |
258 | 0 | } |
259 | | |
260 | 0 | *out_enc_len = X25519_PUBLIC_VALUE_LEN; |
261 | 0 | *out_shared_secret_len = SHA256_DIGEST_LENGTH; |
262 | 0 | return 1; |
263 | 0 | } |
264 | | |
265 | | static int x25519_auth_decap(const EVP_HPKE_KEY *key, |
266 | | uint8_t *out_shared_secret, |
267 | | size_t *out_shared_secret_len, const uint8_t *enc, |
268 | | size_t enc_len, const uint8_t *peer_public_key, |
269 | 0 | size_t peer_public_key_len) { |
270 | 0 | uint8_t dh[2 * X25519_SHARED_KEY_LEN]; |
271 | 0 | if (enc_len != X25519_PUBLIC_VALUE_LEN || |
272 | 0 | peer_public_key_len != X25519_PUBLIC_VALUE_LEN || |
273 | 0 | !X25519(dh, key->private_key, enc) || |
274 | 0 | !X25519(dh + X25519_SHARED_KEY_LEN, key->private_key, peer_public_key)) { |
275 | 0 | OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_PEER_KEY); |
276 | 0 | return 0; |
277 | 0 | } |
278 | | |
279 | 0 | uint8_t kem_context[3 * X25519_PUBLIC_VALUE_LEN]; |
280 | 0 | OPENSSL_memcpy(kem_context, enc, X25519_PUBLIC_VALUE_LEN); |
281 | 0 | OPENSSL_memcpy(kem_context + X25519_PUBLIC_VALUE_LEN, key->public_key, |
282 | 0 | X25519_PUBLIC_VALUE_LEN); |
283 | 0 | OPENSSL_memcpy(kem_context + 2 * X25519_PUBLIC_VALUE_LEN, peer_public_key, |
284 | 0 | X25519_PUBLIC_VALUE_LEN); |
285 | 0 | if (!dhkem_extract_and_expand(key->kem->id, EVP_sha256(), out_shared_secret, |
286 | 0 | SHA256_DIGEST_LENGTH, dh, sizeof(dh), |
287 | 0 | kem_context, sizeof(kem_context))) { |
288 | 0 | return 0; |
289 | 0 | } |
290 | | |
291 | 0 | *out_shared_secret_len = SHA256_DIGEST_LENGTH; |
292 | 0 | return 1; |
293 | 0 | } |
294 | | |
295 | 0 | const EVP_HPKE_KEM *EVP_hpke_x25519_hkdf_sha256(void) { |
296 | 0 | static const EVP_HPKE_KEM kKEM = { |
297 | | /*id=*/EVP_HPKE_DHKEM_X25519_HKDF_SHA256, |
298 | | /*public_key_len=*/X25519_PUBLIC_VALUE_LEN, |
299 | | /*private_key_len=*/X25519_PRIVATE_KEY_LEN, |
300 | | /*seed_len=*/X25519_PRIVATE_KEY_LEN, |
301 | | /*enc_len=*/X25519_PUBLIC_VALUE_LEN, |
302 | 0 | x25519_init_key, |
303 | 0 | x25519_generate_key, |
304 | 0 | x25519_encap_with_seed, |
305 | 0 | x25519_decap, |
306 | 0 | x25519_auth_encap_with_seed, |
307 | 0 | x25519_auth_decap, |
308 | 0 | }; |
309 | 0 | return &kKEM; |
310 | 0 | } |
311 | | |
312 | 0 | uint16_t EVP_HPKE_KEM_id(const EVP_HPKE_KEM *kem) { return kem->id; } |
313 | | |
314 | 0 | size_t EVP_HPKE_KEM_public_key_len(const EVP_HPKE_KEM *kem) { |
315 | 0 | return kem->public_key_len; |
316 | 0 | } |
317 | | |
318 | 0 | size_t EVP_HPKE_KEM_private_key_len(const EVP_HPKE_KEM *kem) { |
319 | 0 | return kem->private_key_len; |
320 | 0 | } |
321 | | |
322 | 0 | size_t EVP_HPKE_KEM_enc_len(const EVP_HPKE_KEM *kem) { return kem->enc_len; } |
323 | | |
324 | 0 | void EVP_HPKE_KEY_zero(EVP_HPKE_KEY *key) { |
325 | 0 | OPENSSL_memset(key, 0, sizeof(EVP_HPKE_KEY)); |
326 | 0 | } |
327 | | |
328 | 0 | void EVP_HPKE_KEY_cleanup(EVP_HPKE_KEY *key) { |
329 | | // Nothing to clean up for now, but we may introduce a cleanup process in the |
330 | | // future. |
331 | 0 | } |
332 | | |
333 | 0 | EVP_HPKE_KEY *EVP_HPKE_KEY_new(void) { |
334 | 0 | EVP_HPKE_KEY *key = OPENSSL_malloc(sizeof(EVP_HPKE_KEY)); |
335 | 0 | if (key == NULL) { |
336 | 0 | return NULL; |
337 | 0 | } |
338 | 0 | EVP_HPKE_KEY_zero(key); |
339 | 0 | return key; |
340 | 0 | } |
341 | | |
342 | 0 | void EVP_HPKE_KEY_free(EVP_HPKE_KEY *key) { |
343 | 0 | if (key != NULL) { |
344 | 0 | EVP_HPKE_KEY_cleanup(key); |
345 | 0 | OPENSSL_free(key); |
346 | 0 | } |
347 | 0 | } |
348 | | |
349 | 0 | int EVP_HPKE_KEY_copy(EVP_HPKE_KEY *dst, const EVP_HPKE_KEY *src) { |
350 | | // For now, |EVP_HPKE_KEY| is trivially copyable. |
351 | 0 | OPENSSL_memcpy(dst, src, sizeof(EVP_HPKE_KEY)); |
352 | 0 | return 1; |
353 | 0 | } |
354 | | |
355 | | int EVP_HPKE_KEY_init(EVP_HPKE_KEY *key, const EVP_HPKE_KEM *kem, |
356 | 0 | const uint8_t *priv_key, size_t priv_key_len) { |
357 | 0 | EVP_HPKE_KEY_zero(key); |
358 | 0 | key->kem = kem; |
359 | 0 | if (!kem->init_key(key, priv_key, priv_key_len)) { |
360 | 0 | key->kem = NULL; |
361 | 0 | return 0; |
362 | 0 | } |
363 | 0 | return 1; |
364 | 0 | } |
365 | | |
366 | 0 | int EVP_HPKE_KEY_generate(EVP_HPKE_KEY *key, const EVP_HPKE_KEM *kem) { |
367 | 0 | EVP_HPKE_KEY_zero(key); |
368 | 0 | key->kem = kem; |
369 | 0 | if (!kem->generate_key(key)) { |
370 | 0 | key->kem = NULL; |
371 | 0 | return 0; |
372 | 0 | } |
373 | 0 | return 1; |
374 | 0 | } |
375 | | |
376 | 0 | const EVP_HPKE_KEM *EVP_HPKE_KEY_kem(const EVP_HPKE_KEY *key) { |
377 | 0 | return key->kem; |
378 | 0 | } |
379 | | |
380 | | int EVP_HPKE_KEY_public_key(const EVP_HPKE_KEY *key, uint8_t *out, |
381 | 0 | size_t *out_len, size_t max_out) { |
382 | 0 | if (max_out < key->kem->public_key_len) { |
383 | 0 | OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_BUFFER_SIZE); |
384 | 0 | return 0; |
385 | 0 | } |
386 | 0 | OPENSSL_memcpy(out, key->public_key, key->kem->public_key_len); |
387 | 0 | *out_len = key->kem->public_key_len; |
388 | 0 | return 1; |
389 | 0 | } |
390 | | |
391 | | int EVP_HPKE_KEY_private_key(const EVP_HPKE_KEY *key, uint8_t *out, |
392 | 0 | size_t *out_len, size_t max_out) { |
393 | 0 | if (max_out < key->kem->private_key_len) { |
394 | 0 | OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_BUFFER_SIZE); |
395 | 0 | return 0; |
396 | 0 | } |
397 | 0 | OPENSSL_memcpy(out, key->private_key, key->kem->private_key_len); |
398 | 0 | *out_len = key->kem->private_key_len; |
399 | 0 | return 1; |
400 | 0 | } |
401 | | |
402 | | |
403 | | // Supported KDFs and AEADs. |
404 | | |
405 | 0 | const EVP_HPKE_KDF *EVP_hpke_hkdf_sha256(void) { |
406 | 0 | static const EVP_HPKE_KDF kKDF = {EVP_HPKE_HKDF_SHA256, &EVP_sha256}; |
407 | 0 | return &kKDF; |
408 | 0 | } |
409 | | |
410 | 0 | uint16_t EVP_HPKE_KDF_id(const EVP_HPKE_KDF *kdf) { return kdf->id; } |
411 | | |
412 | 0 | const EVP_MD *EVP_HPKE_KDF_hkdf_md(const EVP_HPKE_KDF *kdf) { |
413 | 0 | return kdf->hkdf_md_func(); |
414 | 0 | } |
415 | | |
416 | 0 | const EVP_HPKE_AEAD *EVP_hpke_aes_128_gcm(void) { |
417 | 0 | static const EVP_HPKE_AEAD kAEAD = {EVP_HPKE_AES_128_GCM, |
418 | 0 | &EVP_aead_aes_128_gcm}; |
419 | 0 | return &kAEAD; |
420 | 0 | } |
421 | | |
422 | 0 | const EVP_HPKE_AEAD *EVP_hpke_aes_256_gcm(void) { |
423 | 0 | static const EVP_HPKE_AEAD kAEAD = {EVP_HPKE_AES_256_GCM, |
424 | 0 | &EVP_aead_aes_256_gcm}; |
425 | 0 | return &kAEAD; |
426 | 0 | } |
427 | | |
428 | 0 | const EVP_HPKE_AEAD *EVP_hpke_chacha20_poly1305(void) { |
429 | 0 | static const EVP_HPKE_AEAD kAEAD = {EVP_HPKE_CHACHA20_POLY1305, |
430 | 0 | &EVP_aead_chacha20_poly1305}; |
431 | 0 | return &kAEAD; |
432 | 0 | } |
433 | | |
434 | 0 | uint16_t EVP_HPKE_AEAD_id(const EVP_HPKE_AEAD *aead) { return aead->id; } |
435 | | |
436 | 0 | const EVP_AEAD *EVP_HPKE_AEAD_aead(const EVP_HPKE_AEAD *aead) { |
437 | 0 | return aead->aead_func(); |
438 | 0 | } |
439 | | |
440 | | |
441 | | // HPKE implementation. |
442 | | |
443 | | // This is strlen("HPKE") + 3 * sizeof(uint16_t). |
444 | 0 | #define HPKE_SUITE_ID_LEN 10 |
445 | | |
446 | | // The suite_id for non-KEM pieces of HPKE is defined as concat("HPKE", |
447 | | // I2OSP(kem_id, 2), I2OSP(kdf_id, 2), I2OSP(aead_id, 2)). |
448 | | static int hpke_build_suite_id(const EVP_HPKE_CTX *ctx, |
449 | 0 | uint8_t out[HPKE_SUITE_ID_LEN]) { |
450 | 0 | CBB cbb; |
451 | 0 | CBB_init_fixed(&cbb, out, HPKE_SUITE_ID_LEN); |
452 | 0 | return add_label_string(&cbb, "HPKE") && // |
453 | 0 | CBB_add_u16(&cbb, ctx->kem->id) && // |
454 | 0 | CBB_add_u16(&cbb, ctx->kdf->id) && // |
455 | 0 | CBB_add_u16(&cbb, ctx->aead->id); |
456 | 0 | } |
457 | | |
458 | 0 | #define HPKE_MODE_BASE 0 |
459 | 0 | #define HPKE_MODE_AUTH 2 |
460 | | |
461 | | static int hpke_key_schedule(EVP_HPKE_CTX *ctx, uint8_t mode, |
462 | | const uint8_t *shared_secret, |
463 | | size_t shared_secret_len, const uint8_t *info, |
464 | 0 | size_t info_len) { |
465 | 0 | uint8_t suite_id[HPKE_SUITE_ID_LEN]; |
466 | 0 | if (!hpke_build_suite_id(ctx, suite_id)) { |
467 | 0 | return 0; |
468 | 0 | } |
469 | | |
470 | | // psk_id_hash = LabeledExtract("", "psk_id_hash", psk_id) |
471 | | // TODO(davidben): Precompute this value and store it with the EVP_HPKE_KDF. |
472 | 0 | const EVP_MD *hkdf_md = ctx->kdf->hkdf_md_func(); |
473 | 0 | uint8_t psk_id_hash[EVP_MAX_MD_SIZE]; |
474 | 0 | size_t psk_id_hash_len; |
475 | 0 | if (!hpke_labeled_extract(hkdf_md, psk_id_hash, &psk_id_hash_len, NULL, 0, |
476 | 0 | suite_id, sizeof(suite_id), "psk_id_hash", NULL, |
477 | 0 | 0)) { |
478 | 0 | return 0; |
479 | 0 | } |
480 | | |
481 | | // info_hash = LabeledExtract("", "info_hash", info) |
482 | 0 | uint8_t info_hash[EVP_MAX_MD_SIZE]; |
483 | 0 | size_t info_hash_len; |
484 | 0 | if (!hpke_labeled_extract(hkdf_md, info_hash, &info_hash_len, NULL, 0, |
485 | 0 | suite_id, sizeof(suite_id), "info_hash", info, |
486 | 0 | info_len)) { |
487 | 0 | return 0; |
488 | 0 | } |
489 | | |
490 | | // key_schedule_context = concat(mode, psk_id_hash, info_hash) |
491 | 0 | uint8_t context[sizeof(uint8_t) + 2 * EVP_MAX_MD_SIZE]; |
492 | 0 | size_t context_len; |
493 | 0 | CBB context_cbb; |
494 | 0 | CBB_init_fixed(&context_cbb, context, sizeof(context)); |
495 | 0 | if (!CBB_add_u8(&context_cbb, mode) || |
496 | 0 | !CBB_add_bytes(&context_cbb, psk_id_hash, psk_id_hash_len) || |
497 | 0 | !CBB_add_bytes(&context_cbb, info_hash, info_hash_len) || |
498 | 0 | !CBB_finish(&context_cbb, NULL, &context_len)) { |
499 | 0 | return 0; |
500 | 0 | } |
501 | | |
502 | | // secret = LabeledExtract(shared_secret, "secret", psk) |
503 | 0 | uint8_t secret[EVP_MAX_MD_SIZE]; |
504 | 0 | size_t secret_len; |
505 | 0 | if (!hpke_labeled_extract(hkdf_md, secret, &secret_len, shared_secret, |
506 | 0 | shared_secret_len, suite_id, sizeof(suite_id), |
507 | 0 | "secret", NULL, 0)) { |
508 | 0 | return 0; |
509 | 0 | } |
510 | | |
511 | | // key = LabeledExpand(secret, "key", key_schedule_context, Nk) |
512 | 0 | const EVP_AEAD *aead = EVP_HPKE_AEAD_aead(ctx->aead); |
513 | 0 | uint8_t key[EVP_AEAD_MAX_KEY_LENGTH]; |
514 | 0 | const size_t kKeyLen = EVP_AEAD_key_length(aead); |
515 | 0 | if (!hpke_labeled_expand(hkdf_md, key, kKeyLen, secret, secret_len, suite_id, |
516 | 0 | sizeof(suite_id), "key", context, context_len) || |
517 | 0 | !EVP_AEAD_CTX_init(&ctx->aead_ctx, aead, key, kKeyLen, |
518 | 0 | EVP_AEAD_DEFAULT_TAG_LENGTH, NULL)) { |
519 | 0 | return 0; |
520 | 0 | } |
521 | | |
522 | | // base_nonce = LabeledExpand(secret, "base_nonce", key_schedule_context, Nn) |
523 | 0 | if (!hpke_labeled_expand(hkdf_md, ctx->base_nonce, |
524 | 0 | EVP_AEAD_nonce_length(aead), secret, secret_len, |
525 | 0 | suite_id, sizeof(suite_id), "base_nonce", context, |
526 | 0 | context_len)) { |
527 | 0 | return 0; |
528 | 0 | } |
529 | | |
530 | | // exporter_secret = LabeledExpand(secret, "exp", key_schedule_context, Nh) |
531 | 0 | if (!hpke_labeled_expand(hkdf_md, ctx->exporter_secret, EVP_MD_size(hkdf_md), |
532 | 0 | secret, secret_len, suite_id, sizeof(suite_id), |
533 | 0 | "exp", context, context_len)) { |
534 | 0 | return 0; |
535 | 0 | } |
536 | | |
537 | 0 | return 1; |
538 | 0 | } |
539 | | |
540 | 0 | void EVP_HPKE_CTX_zero(EVP_HPKE_CTX *ctx) { |
541 | 0 | OPENSSL_memset(ctx, 0, sizeof(EVP_HPKE_CTX)); |
542 | 0 | EVP_AEAD_CTX_zero(&ctx->aead_ctx); |
543 | 0 | } |
544 | | |
545 | 0 | void EVP_HPKE_CTX_cleanup(EVP_HPKE_CTX *ctx) { |
546 | 0 | EVP_AEAD_CTX_cleanup(&ctx->aead_ctx); |
547 | 0 | } |
548 | | |
549 | 0 | EVP_HPKE_CTX *EVP_HPKE_CTX_new(void) { |
550 | 0 | EVP_HPKE_CTX *ctx = OPENSSL_malloc(sizeof(EVP_HPKE_CTX)); |
551 | 0 | if (ctx == NULL) { |
552 | 0 | return NULL; |
553 | 0 | } |
554 | 0 | EVP_HPKE_CTX_zero(ctx); |
555 | 0 | return ctx; |
556 | 0 | } |
557 | | |
558 | 0 | void EVP_HPKE_CTX_free(EVP_HPKE_CTX *ctx) { |
559 | 0 | if (ctx != NULL) { |
560 | 0 | EVP_HPKE_CTX_cleanup(ctx); |
561 | 0 | OPENSSL_free(ctx); |
562 | 0 | } |
563 | 0 | } |
564 | | |
565 | | int EVP_HPKE_CTX_setup_sender(EVP_HPKE_CTX *ctx, uint8_t *out_enc, |
566 | | size_t *out_enc_len, size_t max_enc, |
567 | | const EVP_HPKE_KEM *kem, const EVP_HPKE_KDF *kdf, |
568 | | const EVP_HPKE_AEAD *aead, |
569 | | const uint8_t *peer_public_key, |
570 | | size_t peer_public_key_len, const uint8_t *info, |
571 | 0 | size_t info_len) { |
572 | 0 | uint8_t seed[MAX_SEED_LEN]; |
573 | 0 | RAND_bytes(seed, kem->seed_len); |
574 | 0 | return EVP_HPKE_CTX_setup_sender_with_seed_for_testing( |
575 | 0 | ctx, out_enc, out_enc_len, max_enc, kem, kdf, aead, peer_public_key, |
576 | 0 | peer_public_key_len, info, info_len, seed, kem->seed_len); |
577 | 0 | } |
578 | | |
579 | | int EVP_HPKE_CTX_setup_sender_with_seed_for_testing( |
580 | | EVP_HPKE_CTX *ctx, uint8_t *out_enc, size_t *out_enc_len, size_t max_enc, |
581 | | const EVP_HPKE_KEM *kem, const EVP_HPKE_KDF *kdf, const EVP_HPKE_AEAD *aead, |
582 | | const uint8_t *peer_public_key, size_t peer_public_key_len, |
583 | | const uint8_t *info, size_t info_len, const uint8_t *seed, |
584 | 0 | size_t seed_len) { |
585 | 0 | EVP_HPKE_CTX_zero(ctx); |
586 | 0 | ctx->is_sender = 1; |
587 | 0 | ctx->kem = kem; |
588 | 0 | ctx->kdf = kdf; |
589 | 0 | ctx->aead = aead; |
590 | 0 | uint8_t shared_secret[MAX_SHARED_SECRET_LEN]; |
591 | 0 | size_t shared_secret_len; |
592 | 0 | if (!kem->encap_with_seed(kem, shared_secret, &shared_secret_len, out_enc, |
593 | 0 | out_enc_len, max_enc, peer_public_key, |
594 | 0 | peer_public_key_len, seed, seed_len) || |
595 | 0 | !hpke_key_schedule(ctx, HPKE_MODE_BASE, shared_secret, shared_secret_len, |
596 | 0 | info, info_len)) { |
597 | 0 | EVP_HPKE_CTX_cleanup(ctx); |
598 | 0 | return 0; |
599 | 0 | } |
600 | 0 | return 1; |
601 | 0 | } |
602 | | |
603 | | int EVP_HPKE_CTX_setup_recipient(EVP_HPKE_CTX *ctx, const EVP_HPKE_KEY *key, |
604 | | const EVP_HPKE_KDF *kdf, |
605 | | const EVP_HPKE_AEAD *aead, const uint8_t *enc, |
606 | | size_t enc_len, const uint8_t *info, |
607 | 0 | size_t info_len) { |
608 | 0 | EVP_HPKE_CTX_zero(ctx); |
609 | 0 | ctx->is_sender = 0; |
610 | 0 | ctx->kem = key->kem; |
611 | 0 | ctx->kdf = kdf; |
612 | 0 | ctx->aead = aead; |
613 | 0 | uint8_t shared_secret[MAX_SHARED_SECRET_LEN]; |
614 | 0 | size_t shared_secret_len; |
615 | 0 | if (!key->kem->decap(key, shared_secret, &shared_secret_len, enc, enc_len) || |
616 | 0 | !hpke_key_schedule(ctx, HPKE_MODE_BASE, shared_secret, shared_secret_len, |
617 | 0 | info, info_len)) { |
618 | 0 | EVP_HPKE_CTX_cleanup(ctx); |
619 | 0 | return 0; |
620 | 0 | } |
621 | 0 | return 1; |
622 | 0 | } |
623 | | |
624 | | |
625 | | int EVP_HPKE_CTX_setup_auth_sender( |
626 | | EVP_HPKE_CTX *ctx, uint8_t *out_enc, size_t *out_enc_len, size_t max_enc, |
627 | | const EVP_HPKE_KEY *key, const EVP_HPKE_KDF *kdf, const EVP_HPKE_AEAD *aead, |
628 | | const uint8_t *peer_public_key, size_t peer_public_key_len, |
629 | 0 | const uint8_t *info, size_t info_len) { |
630 | 0 | uint8_t seed[MAX_SEED_LEN]; |
631 | 0 | RAND_bytes(seed, key->kem->seed_len); |
632 | 0 | return EVP_HPKE_CTX_setup_auth_sender_with_seed_for_testing( |
633 | 0 | ctx, out_enc, out_enc_len, max_enc, key, kdf, aead, peer_public_key, |
634 | 0 | peer_public_key_len, info, info_len, seed, key->kem->seed_len); |
635 | 0 | } |
636 | | |
637 | | int EVP_HPKE_CTX_setup_auth_sender_with_seed_for_testing( |
638 | | EVP_HPKE_CTX *ctx, uint8_t *out_enc, size_t *out_enc_len, size_t max_enc, |
639 | | const EVP_HPKE_KEY *key, const EVP_HPKE_KDF *kdf, const EVP_HPKE_AEAD *aead, |
640 | | const uint8_t *peer_public_key, size_t peer_public_key_len, |
641 | | const uint8_t *info, size_t info_len, const uint8_t *seed, |
642 | 0 | size_t seed_len) { |
643 | 0 | if (key->kem->auth_encap_with_seed == NULL) { |
644 | | // Not all HPKE KEMs support AuthEncap. |
645 | 0 | OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE); |
646 | 0 | return 0; |
647 | 0 | } |
648 | | |
649 | 0 | EVP_HPKE_CTX_zero(ctx); |
650 | 0 | ctx->is_sender = 1; |
651 | 0 | ctx->kem = key->kem; |
652 | 0 | ctx->kdf = kdf; |
653 | 0 | ctx->aead = aead; |
654 | 0 | uint8_t shared_secret[MAX_SHARED_SECRET_LEN]; |
655 | 0 | size_t shared_secret_len; |
656 | 0 | if (!key->kem->auth_encap_with_seed( |
657 | 0 | key, shared_secret, &shared_secret_len, out_enc, out_enc_len, max_enc, |
658 | 0 | peer_public_key, peer_public_key_len, seed, seed_len) || |
659 | 0 | !hpke_key_schedule(ctx, HPKE_MODE_AUTH, shared_secret, shared_secret_len, |
660 | 0 | info, info_len)) { |
661 | 0 | EVP_HPKE_CTX_cleanup(ctx); |
662 | 0 | return 0; |
663 | 0 | } |
664 | 0 | return 1; |
665 | 0 | } |
666 | | |
667 | | int EVP_HPKE_CTX_setup_auth_recipient( |
668 | | EVP_HPKE_CTX *ctx, const EVP_HPKE_KEY *key, const EVP_HPKE_KDF *kdf, |
669 | | const EVP_HPKE_AEAD *aead, const uint8_t *enc, size_t enc_len, |
670 | | const uint8_t *info, size_t info_len, const uint8_t *peer_public_key, |
671 | 0 | size_t peer_public_key_len) { |
672 | 0 | if (key->kem->auth_decap == NULL) { |
673 | | // Not all HPKE KEMs support AuthDecap. |
674 | 0 | OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE); |
675 | 0 | return 0; |
676 | 0 | } |
677 | | |
678 | 0 | EVP_HPKE_CTX_zero(ctx); |
679 | 0 | ctx->is_sender = 0; |
680 | 0 | ctx->kem = key->kem; |
681 | 0 | ctx->kdf = kdf; |
682 | 0 | ctx->aead = aead; |
683 | 0 | uint8_t shared_secret[MAX_SHARED_SECRET_LEN]; |
684 | 0 | size_t shared_secret_len; |
685 | 0 | if (!key->kem->auth_decap(key, shared_secret, &shared_secret_len, enc, |
686 | 0 | enc_len, peer_public_key, peer_public_key_len) || |
687 | 0 | !hpke_key_schedule(ctx, HPKE_MODE_AUTH, shared_secret, shared_secret_len, |
688 | 0 | info, info_len)) { |
689 | 0 | EVP_HPKE_CTX_cleanup(ctx); |
690 | 0 | return 0; |
691 | 0 | } |
692 | 0 | return 1; |
693 | 0 | } |
694 | | |
695 | | static void hpke_nonce(const EVP_HPKE_CTX *ctx, uint8_t *out_nonce, |
696 | 0 | size_t nonce_len) { |
697 | 0 | assert(nonce_len >= 8); |
698 | | |
699 | | // Write padded big-endian bytes of |ctx->seq| to |out_nonce|. |
700 | 0 | OPENSSL_memset(out_nonce, 0, nonce_len); |
701 | 0 | uint64_t seq_copy = ctx->seq; |
702 | 0 | for (size_t i = 0; i < 8; i++) { |
703 | 0 | out_nonce[nonce_len - i - 1] = seq_copy & 0xff; |
704 | 0 | seq_copy >>= 8; |
705 | 0 | } |
706 | | |
707 | | // XOR the encoded sequence with the |ctx->base_nonce|. |
708 | 0 | for (size_t i = 0; i < nonce_len; i++) { |
709 | 0 | out_nonce[i] ^= ctx->base_nonce[i]; |
710 | 0 | } |
711 | 0 | } |
712 | | |
713 | | int EVP_HPKE_CTX_open(EVP_HPKE_CTX *ctx, uint8_t *out, size_t *out_len, |
714 | | size_t max_out_len, const uint8_t *in, size_t in_len, |
715 | 0 | const uint8_t *ad, size_t ad_len) { |
716 | 0 | if (ctx->is_sender) { |
717 | 0 | OPENSSL_PUT_ERROR(EVP, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); |
718 | 0 | return 0; |
719 | 0 | } |
720 | 0 | if (ctx->seq == UINT64_MAX) { |
721 | 0 | OPENSSL_PUT_ERROR(EVP, ERR_R_OVERFLOW); |
722 | 0 | return 0; |
723 | 0 | } |
724 | | |
725 | 0 | uint8_t nonce[EVP_AEAD_MAX_NONCE_LENGTH]; |
726 | 0 | const size_t nonce_len = EVP_AEAD_nonce_length(ctx->aead_ctx.aead); |
727 | 0 | hpke_nonce(ctx, nonce, nonce_len); |
728 | |
|
729 | 0 | if (!EVP_AEAD_CTX_open(&ctx->aead_ctx, out, out_len, max_out_len, nonce, |
730 | 0 | nonce_len, in, in_len, ad, ad_len)) { |
731 | 0 | return 0; |
732 | 0 | } |
733 | 0 | ctx->seq++; |
734 | 0 | return 1; |
735 | 0 | } |
736 | | |
737 | | int EVP_HPKE_CTX_seal(EVP_HPKE_CTX *ctx, uint8_t *out, size_t *out_len, |
738 | | size_t max_out_len, const uint8_t *in, size_t in_len, |
739 | 0 | const uint8_t *ad, size_t ad_len) { |
740 | 0 | if (!ctx->is_sender) { |
741 | 0 | OPENSSL_PUT_ERROR(EVP, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); |
742 | 0 | return 0; |
743 | 0 | } |
744 | 0 | if (ctx->seq == UINT64_MAX) { |
745 | 0 | OPENSSL_PUT_ERROR(EVP, ERR_R_OVERFLOW); |
746 | 0 | return 0; |
747 | 0 | } |
748 | | |
749 | 0 | uint8_t nonce[EVP_AEAD_MAX_NONCE_LENGTH]; |
750 | 0 | const size_t nonce_len = EVP_AEAD_nonce_length(ctx->aead_ctx.aead); |
751 | 0 | hpke_nonce(ctx, nonce, nonce_len); |
752 | |
|
753 | 0 | if (!EVP_AEAD_CTX_seal(&ctx->aead_ctx, out, out_len, max_out_len, nonce, |
754 | 0 | nonce_len, in, in_len, ad, ad_len)) { |
755 | 0 | return 0; |
756 | 0 | } |
757 | 0 | ctx->seq++; |
758 | 0 | return 1; |
759 | 0 | } |
760 | | |
761 | | int EVP_HPKE_CTX_export(const EVP_HPKE_CTX *ctx, uint8_t *out, |
762 | | size_t secret_len, const uint8_t *context, |
763 | 0 | size_t context_len) { |
764 | 0 | uint8_t suite_id[HPKE_SUITE_ID_LEN]; |
765 | 0 | if (!hpke_build_suite_id(ctx, suite_id)) { |
766 | 0 | return 0; |
767 | 0 | } |
768 | 0 | const EVP_MD *hkdf_md = ctx->kdf->hkdf_md_func(); |
769 | 0 | if (!hpke_labeled_expand(hkdf_md, out, secret_len, ctx->exporter_secret, |
770 | 0 | EVP_MD_size(hkdf_md), suite_id, sizeof(suite_id), |
771 | 0 | "sec", context, context_len)) { |
772 | 0 | return 0; |
773 | 0 | } |
774 | 0 | return 1; |
775 | 0 | } |
776 | | |
777 | 0 | size_t EVP_HPKE_CTX_max_overhead(const EVP_HPKE_CTX *ctx) { |
778 | 0 | assert(ctx->is_sender); |
779 | 0 | return EVP_AEAD_max_overhead(EVP_AEAD_CTX_aead(&ctx->aead_ctx)); |
780 | 0 | } |
781 | | |
782 | 0 | const EVP_HPKE_KEM *EVP_HPKE_CTX_kem(const EVP_HPKE_CTX *ctx) { |
783 | 0 | return ctx->kem; |
784 | 0 | } |
785 | | |
786 | 0 | const EVP_HPKE_AEAD *EVP_HPKE_CTX_aead(const EVP_HPKE_CTX *ctx) { |
787 | 0 | return ctx->aead; |
788 | 0 | } |
789 | | |
790 | 0 | const EVP_HPKE_KDF *EVP_HPKE_CTX_kdf(const EVP_HPKE_CTX *ctx) { |
791 | 0 | return ctx->kdf; |
792 | 0 | } |