/src/boringssl/crypto/hpke/hpke.cc
Line | Count | Source |
1 | | // Copyright 2020 The BoringSSL Authors |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | | // you may not use this file except in compliance with the License. |
5 | | // You may obtain a copy of the License at |
6 | | // |
7 | | // https://www.apache.org/licenses/LICENSE-2.0 |
8 | | // |
9 | | // Unless required by applicable law or agreed to in writing, software |
10 | | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | | // See the License for the specific language governing permissions and |
13 | | // limitations under the License. |
14 | | |
15 | | #include <openssl/hpke.h> |
16 | | |
17 | | #include <assert.h> |
18 | | #include <string.h> |
19 | | |
20 | | #include <array> |
21 | | #include <string_view> |
22 | | |
23 | | #include <openssl/aead.h> |
24 | | #include <openssl/bytestring.h> |
25 | | #include <openssl/curve25519.h> |
26 | | #include <openssl/digest.h> |
27 | | #include <openssl/ec.h> |
28 | | #include <openssl/err.h> |
29 | | #include <openssl/evp.h> |
30 | | #include <openssl/evp_errors.h> |
31 | | #include <openssl/hkdf.h> |
32 | | #include <openssl/mem.h> |
33 | | #include <openssl/mlkem.h> |
34 | | #include <openssl/rand.h> |
35 | | #include <openssl/sha2.h> |
36 | | #include <openssl/span.h> |
37 | | #include <openssl/xwing.h> |
38 | | |
39 | | #include "../fipsmodule/ec/internal.h" |
40 | | #include "../fipsmodule/keccak/internal.h" |
41 | | #include "../internal.h" |
42 | | #include "../mem_internal.h" |
43 | | |
44 | | |
45 | | using namespace bssl; |
46 | | |
47 | | // This file implements RFC 9180. |
48 | | |
49 | | #define MAX_SEED_LEN XWING_SEED_LEN |
50 | | #define MAX_SHARED_SECRET_LEN SHA256_DIGEST_LENGTH |
51 | | |
52 | | namespace { |
53 | | |
54 | | // HpkeKeyMethod implements the key-type-specific operations that HPKE performs |
55 | | // on keys of a particular `EVP_PKEY_ALG` type. |
56 | | struct HpkeKeyMethod { |
57 | | // hpke_kem_id is the ID of the KEM in RFC 9180. |
58 | | uint16_t hpke_kem_id; |
59 | | |
60 | | // public_key_len corresponds to Npk in RFC 9180. |
61 | | size_t public_key_len; |
62 | | // private_key_len corresponds to Nsk in RFC 9180. |
63 | | size_t private_key_len; |
64 | | |
65 | | const EVP_PKEY_ALG *(*alg_func)(); |
66 | | |
67 | | // deserialize_private_key and serialize_private_key implement the |
68 | | // DeserializePrivateKey and SerializePrivateKey operations for the key type |
69 | | // from section 4 of RFC 9180, using the same format accepted by |
70 | | // `EVP_HPKE_KEY_init`. |
71 | | EVP_PKEY *(*deserialize_private_key)(const EVP_PKEY_ALG *alg, |
72 | | const uint8_t *in, size_t len); |
73 | | int (*serialize_private_key)(const EVP_PKEY *pkey, uint8_t *out, |
74 | | size_t *out_len); |
75 | | |
76 | | // deserialize_public_key and serialize_public_key implement the |
77 | | // DeserializePublicKey and SerializePublicKey operations from section 4 of |
78 | | // RFC 9180. |
79 | | EVP_PKEY *(*deserialize_public_key)(const EVP_PKEY_ALG *alg, |
80 | | const uint8_t *in, size_t len); |
81 | | int (*serialize_public_key)(const EVP_PKEY *pkey, uint8_t *out, |
82 | | size_t *out_len); |
83 | | |
84 | | // derive_key_pair implements the DeriveKeyPair operation from section 4 of |
85 | | // RFC 9180. |
86 | | EVP_PKEY *(*derive_key_pair)(const EVP_PKEY_ALG *alg, uint16_t kem_suite_id, |
87 | | Span<const uint8_t> ikm); |
88 | | }; |
89 | | |
90 | | } // namespace |
91 | | |
92 | | struct evp_hpke_kem_st { |
93 | | uint16_t id; |
94 | | |
95 | | // evp_kem_func and key_method, if non-null, provide the EVP_PKEY_CTX and |
96 | | // EVP_KEM functionality backing this EVP_HPKE_KEM. If these are non-null, |
97 | | // then (until fully migrated): |
98 | | // * `init_key`, `generate_key`, and `derive_key` are null (these operations |
99 | | // are supplied by `key_method`), |
100 | | // * `encap_with_seed`, and `decap` are null (these operations are supplied |
101 | | // by the `EVP_KEM`), |
102 | | // * `public_key_len` and `private_key_len` are zero (they are supplied by |
103 | | // `key_method`), and |
104 | | // * `seed_len` and `enc_len` are zero (they are supplied by the `EVP_KEM`). |
105 | | // TODO(crbug.com/535883377): Unify EVP_HPKE_KEM and EVP_KEM for all supported |
106 | | // HPKE KEMs. |
107 | | const EVP_KEM *(*evp_kem_func)(); |
108 | | const HpkeKeyMethod *key_method; |
109 | | |
110 | | size_t public_key_len; |
111 | | size_t private_key_len; |
112 | | size_t seed_len; |
113 | | size_t enc_len; |
114 | | int (*init_key)(EVP_HPKE_KEY *key, const uint8_t *priv_key, |
115 | | size_t priv_key_len); |
116 | | int (*generate_key)(EVP_HPKE_KEY *key); |
117 | | int (*derive_key)(EVP_HPKE_KEY *key, Span<const uint8_t> ikm); |
118 | | int (*encap_with_seed)(const EVP_HPKE_KEM *kem, uint8_t *out_shared_secret, |
119 | | size_t *out_shared_secret_len, uint8_t *out_enc, |
120 | | size_t *out_enc_len, size_t max_enc, |
121 | | const uint8_t *peer_public_key, |
122 | | size_t peer_public_key_len, const uint8_t *seed, |
123 | | size_t seed_len); |
124 | | int (*decap)(const EVP_HPKE_KEY *key, uint8_t *out_shared_secret, |
125 | | size_t *out_shared_secret_len, const uint8_t *enc, |
126 | | size_t enc_len); |
127 | | int (*auth_encap_with_seed)(const EVP_HPKE_KEY *key, |
128 | | uint8_t *out_shared_secret, |
129 | | size_t *out_shared_secret_len, uint8_t *out_enc, |
130 | | size_t *out_enc_len, size_t max_enc, |
131 | | const uint8_t *peer_public_key, |
132 | | size_t peer_public_key_len, const uint8_t *seed, |
133 | | size_t seed_len); |
134 | | int (*auth_decap)(const EVP_HPKE_KEY *key, uint8_t *out_shared_secret, |
135 | | size_t *out_shared_secret_len, const uint8_t *enc, |
136 | | size_t enc_len, const uint8_t *peer_public_key, |
137 | | size_t peer_public_key_len); |
138 | | }; |
139 | | |
140 | | struct evp_hpke_kdf_st { |
141 | | uint16_t id; |
142 | | // We only support HKDF-based KDFs. |
143 | | const EVP_MD *(*hkdf_md_func)(); |
144 | | }; |
145 | | |
146 | | struct evp_hpke_aead_st { |
147 | | uint16_t id; |
148 | | const EVP_AEAD *(*aead_func)(); |
149 | | }; |
150 | | |
151 | | |
152 | | // Low-level labeled KDF functions. |
153 | | |
154 | | static const char kHpkeVersionId[] = "HPKE-v1"; |
155 | | |
156 | 4.65k | static int add_label_string(CBB *cbb, const char *label) { |
157 | 4.65k | return CBB_add_bytes(cbb, (const uint8_t *)label, strlen(label)); |
158 | 4.65k | } |
159 | | |
160 | | static int hpke_labeled_extract(const EVP_MD *hkdf_md, uint8_t *out_key, |
161 | | size_t *out_len, const uint8_t *salt, |
162 | | size_t salt_len, const uint8_t *suite_id, |
163 | | size_t suite_id_len, const char *label, |
164 | 1.16k | const uint8_t *ikm, size_t ikm_len) { |
165 | | // labeledIKM = concat("HPKE-v1", suite_id, label, IKM) |
166 | 1.16k | CBB labeled_ikm; |
167 | 1.16k | int ok = CBB_init(&labeled_ikm, 0) && |
168 | 1.16k | add_label_string(&labeled_ikm, kHpkeVersionId) && |
169 | 1.16k | CBB_add_bytes(&labeled_ikm, suite_id, suite_id_len) && |
170 | 1.16k | add_label_string(&labeled_ikm, label) && |
171 | 1.16k | CBB_add_bytes(&labeled_ikm, ikm, ikm_len) && |
172 | 1.16k | HKDF_extract(out_key, out_len, hkdf_md, CBB_data(&labeled_ikm), |
173 | 1.16k | CBB_len(&labeled_ikm), salt, salt_len); |
174 | 1.16k | CBB_cleanup(&labeled_ikm); |
175 | 1.16k | return ok; |
176 | 1.16k | } |
177 | | |
178 | | static int hpke_labeled_expand(const EVP_MD *hkdf_md, uint8_t *out_key, |
179 | | size_t out_len, const uint8_t *prk, |
180 | | size_t prk_len, const uint8_t *suite_id, |
181 | | size_t suite_id_len, const char *label, |
182 | 1.16k | const uint8_t *info, size_t info_len) { |
183 | | // labeledInfo = concat(I2OSP(L, 2), "HPKE-v1", suite_id, label, info) |
184 | 1.16k | CBB labeled_info; |
185 | 1.16k | int ok = CBB_init(&labeled_info, 0) && // |
186 | 1.16k | CBB_add_u16(&labeled_info, out_len) && |
187 | 1.16k | add_label_string(&labeled_info, kHpkeVersionId) && |
188 | 1.16k | CBB_add_bytes(&labeled_info, suite_id, suite_id_len) && |
189 | 1.16k | add_label_string(&labeled_info, label) && |
190 | 1.16k | CBB_add_bytes(&labeled_info, info, info_len) && |
191 | 1.16k | HKDF_expand(out_key, out_len, hkdf_md, prk, prk_len, |
192 | 1.16k | CBB_data(&labeled_info), CBB_len(&labeled_info)); |
193 | 1.16k | CBB_cleanup(&labeled_info); |
194 | 1.16k | return ok; |
195 | 1.16k | } |
196 | | |
197 | 0 | static void absorb_u16(BORINGSSL_keccak_st *ctx, uint16_t n) { |
198 | 0 | uint8_t bytes[2]; |
199 | 0 | CRYPTO_store_u16_be(bytes, n); |
200 | 0 | BORINGSSL_keccak_absorb(ctx, bytes, sizeof(bytes)); |
201 | 0 | } |
202 | | |
203 | | static void hpke_shake256_labeled_derive(Span<uint8_t> out, |
204 | | Span<const uint8_t> ikm, |
205 | | Span<const uint8_t> suite_id, |
206 | | std::string_view label, |
207 | 0 | Span<const uint8_t> context) { |
208 | | // https://www.ietf.org/archive/id/draft-ietf-hpke-hpke-03.html#name-labeled-derivation-function |
209 | | // https://www.ietf.org/archive/id/draft-ietf-hpke-pq-04.html#name-single-stage-kdfs |
210 | 0 | BORINGSSL_keccak_st ctx; |
211 | 0 | BORINGSSL_keccak_init(&ctx, boringssl_shake256); |
212 | 0 | BORINGSSL_keccak_absorb(&ctx, ikm.data(), ikm.size()); |
213 | 0 | auto hpke_version_id = StringAsBytes(kHpkeVersionId); |
214 | 0 | BORINGSSL_keccak_absorb(&ctx, hpke_version_id.data(), hpke_version_id.size()); |
215 | 0 | BORINGSSL_keccak_absorb(&ctx, suite_id.data(), suite_id.size()); |
216 | 0 | auto label_bytes = StringAsBytes(label); |
217 | 0 | assert(label_bytes.size() < 0xffff); |
218 | 0 | absorb_u16(&ctx, static_cast<uint16_t>(label_bytes.size())); |
219 | 0 | BORINGSSL_keccak_absorb(&ctx, label_bytes.data(), label_bytes.size()); |
220 | 0 | assert(out.size() <= 0xffff); |
221 | 0 | absorb_u16(&ctx, static_cast<uint16_t>(out.size())); |
222 | 0 | BORINGSSL_keccak_absorb(&ctx, context.data(), context.size()); |
223 | 0 | BORINGSSL_keccak_squeeze(&ctx, out.data(), out.size()); |
224 | 0 | } |
225 | | |
226 | | |
227 | | // KEM implementations. |
228 | | |
229 | 291 | static std::array<uint8_t, 5> hpke_kem_suite_id(uint16_t kem_id) { |
230 | | // concat("KEM", I2OSP(kem_id, 2)) |
231 | 291 | return {'K', 'E', 'M', static_cast<uint8_t>(kem_id >> 8), |
232 | 291 | static_cast<uint8_t>(kem_id)}; |
233 | 291 | } |
234 | | |
235 | | // dhkem_extract_and_expand implements the ExtractAndExpand operation in the |
236 | | // DHKEM construction. See section 4.1 of RFC 9180. |
237 | | static int dhkem_extract_and_expand(uint16_t kem_id, const EVP_MD *hkdf_md, |
238 | | uint8_t *out_key, size_t out_len, |
239 | | const uint8_t *dh, size_t dh_len, |
240 | | const uint8_t *kem_context, |
241 | 291 | size_t kem_context_len) { |
242 | 291 | auto suite_id = hpke_kem_suite_id(kem_id); |
243 | 291 | uint8_t prk[EVP_MAX_MD_SIZE]; |
244 | 291 | size_t prk_len; |
245 | 291 | return hpke_labeled_extract(hkdf_md, prk, &prk_len, nullptr, 0, |
246 | 291 | suite_id.data(), suite_id.size(), "eae_prk", dh, |
247 | 291 | dh_len) && |
248 | 291 | hpke_labeled_expand(hkdf_md, out_key, out_len, prk, prk_len, |
249 | 291 | suite_id.data(), suite_id.size(), "shared_secret", |
250 | 291 | kem_context, kem_context_len); |
251 | 291 | } |
252 | | |
253 | | static int x25519_init_key(EVP_HPKE_KEY *key, const uint8_t *priv_key, |
254 | 13.6k | size_t priv_key_len) { |
255 | 13.6k | if (priv_key_len != X25519_PRIVATE_KEY_LEN) { |
256 | 802 | OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR); |
257 | 802 | return 0; |
258 | 802 | } |
259 | | |
260 | 12.8k | OPENSSL_memcpy(key->private_key, priv_key, priv_key_len); |
261 | 12.8k | X25519_public_from_private(key->public_key, priv_key); |
262 | 12.8k | return 1; |
263 | 13.6k | } |
264 | | |
265 | 0 | static int x25519_generate_key(EVP_HPKE_KEY *key) { |
266 | 0 | X25519_keypair(key->public_key, key->private_key); |
267 | 0 | return 1; |
268 | 0 | } |
269 | | |
270 | 0 | static int x25519_derive_key(EVP_HPKE_KEY *key, Span<const uint8_t> ikm) { |
271 | | // https://www.rfc-editor.org/rfc/rfc9180.html#name-derivekeypair |
272 | 0 | auto suite_id = hpke_kem_suite_id(EVP_HPKE_DHKEM_X25519_HKDF_SHA256); |
273 | 0 | uint8_t dkp_prk[SHA256_DIGEST_LENGTH]; |
274 | 0 | size_t dkp_prk_len; |
275 | 0 | uint8_t sk[32]; |
276 | 0 | if (!hpke_labeled_extract(EVP_sha256(), dkp_prk, &dkp_prk_len, nullptr, 0, |
277 | 0 | suite_id.data(), suite_id.size(), "dkp_prk", |
278 | 0 | ikm.data(), ikm.size()) || |
279 | 0 | !hpke_labeled_expand(EVP_sha256(), sk, sizeof(sk), dkp_prk, dkp_prk_len, |
280 | 0 | suite_id.data(), suite_id.size(), "sk", |
281 | 0 | /*info=*/nullptr, /*info_len=*/0)) { |
282 | 0 | return 0; |
283 | 0 | } |
284 | 0 | return x25519_init_key(key, sk, sizeof(sk)); |
285 | 0 | } |
286 | | |
287 | | static int x25519_encap_with_seed( |
288 | | const EVP_HPKE_KEM *kem, uint8_t *out_shared_secret, |
289 | | size_t *out_shared_secret_len, uint8_t *out_enc, size_t *out_enc_len, |
290 | | size_t max_enc, const uint8_t *peer_public_key, size_t peer_public_key_len, |
291 | 0 | const uint8_t *seed, size_t seed_len) { |
292 | 0 | if (max_enc < X25519_PUBLIC_VALUE_LEN) { |
293 | 0 | OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_BUFFER_SIZE); |
294 | 0 | return 0; |
295 | 0 | } |
296 | 0 | if (seed_len != X25519_PRIVATE_KEY_LEN) { |
297 | 0 | OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR); |
298 | 0 | return 0; |
299 | 0 | } |
300 | 0 | X25519_public_from_private(out_enc, seed); |
301 | |
|
302 | 0 | uint8_t dh[X25519_SHARED_KEY_LEN]; |
303 | 0 | if (peer_public_key_len != X25519_PUBLIC_VALUE_LEN || |
304 | 0 | !X25519(dh, seed, peer_public_key)) { |
305 | 0 | OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_PEER_KEY); |
306 | 0 | return 0; |
307 | 0 | } |
308 | | |
309 | 0 | uint8_t kem_context[2 * X25519_PUBLIC_VALUE_LEN]; |
310 | 0 | OPENSSL_memcpy(kem_context, out_enc, X25519_PUBLIC_VALUE_LEN); |
311 | 0 | OPENSSL_memcpy(kem_context + X25519_PUBLIC_VALUE_LEN, peer_public_key, |
312 | 0 | X25519_PUBLIC_VALUE_LEN); |
313 | 0 | if (!dhkem_extract_and_expand(kem->id, EVP_sha256(), out_shared_secret, |
314 | 0 | SHA256_DIGEST_LENGTH, dh, sizeof(dh), |
315 | 0 | kem_context, sizeof(kem_context))) { |
316 | 0 | return 0; |
317 | 0 | } |
318 | | |
319 | 0 | *out_enc_len = X25519_PUBLIC_VALUE_LEN; |
320 | 0 | *out_shared_secret_len = SHA256_DIGEST_LENGTH; |
321 | 0 | return 1; |
322 | 0 | } |
323 | | |
324 | | static int x25519_decap(const EVP_HPKE_KEY *key, uint8_t *out_shared_secret, |
325 | | size_t *out_shared_secret_len, const uint8_t *enc, |
326 | 303 | size_t enc_len) { |
327 | 303 | uint8_t dh[X25519_SHARED_KEY_LEN]; |
328 | 303 | if (enc_len != X25519_PUBLIC_VALUE_LEN || |
329 | 294 | !X25519(dh, key->private_key, enc)) { |
330 | 12 | OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_PEER_KEY); |
331 | 12 | return 0; |
332 | 12 | } |
333 | | |
334 | 291 | uint8_t kem_context[2 * X25519_PUBLIC_VALUE_LEN]; |
335 | 291 | OPENSSL_memcpy(kem_context, enc, X25519_PUBLIC_VALUE_LEN); |
336 | 291 | OPENSSL_memcpy(kem_context + X25519_PUBLIC_VALUE_LEN, key->public_key, |
337 | 291 | X25519_PUBLIC_VALUE_LEN); |
338 | 291 | if (!dhkem_extract_and_expand(key->kem->id, EVP_sha256(), out_shared_secret, |
339 | 291 | SHA256_DIGEST_LENGTH, dh, sizeof(dh), |
340 | 291 | kem_context, sizeof(kem_context))) { |
341 | 0 | return 0; |
342 | 0 | } |
343 | | |
344 | 291 | *out_shared_secret_len = SHA256_DIGEST_LENGTH; |
345 | 291 | return 1; |
346 | 291 | } |
347 | | |
348 | | static int x25519_auth_encap_with_seed( |
349 | | const EVP_HPKE_KEY *key, uint8_t *out_shared_secret, |
350 | | size_t *out_shared_secret_len, uint8_t *out_enc, size_t *out_enc_len, |
351 | | size_t max_enc, const uint8_t *peer_public_key, size_t peer_public_key_len, |
352 | 0 | const uint8_t *seed, size_t seed_len) { |
353 | 0 | if (max_enc < X25519_PUBLIC_VALUE_LEN) { |
354 | 0 | OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_BUFFER_SIZE); |
355 | 0 | return 0; |
356 | 0 | } |
357 | 0 | if (seed_len != X25519_PRIVATE_KEY_LEN) { |
358 | 0 | OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR); |
359 | 0 | return 0; |
360 | 0 | } |
361 | 0 | X25519_public_from_private(out_enc, seed); |
362 | |
|
363 | 0 | uint8_t dh[2 * X25519_SHARED_KEY_LEN]; |
364 | 0 | if (peer_public_key_len != X25519_PUBLIC_VALUE_LEN || |
365 | 0 | !X25519(dh, seed, peer_public_key) || |
366 | 0 | !X25519(dh + X25519_SHARED_KEY_LEN, key->private_key, peer_public_key)) { |
367 | 0 | OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_PEER_KEY); |
368 | 0 | return 0; |
369 | 0 | } |
370 | | |
371 | 0 | uint8_t kem_context[3 * X25519_PUBLIC_VALUE_LEN]; |
372 | 0 | OPENSSL_memcpy(kem_context, out_enc, X25519_PUBLIC_VALUE_LEN); |
373 | 0 | OPENSSL_memcpy(kem_context + X25519_PUBLIC_VALUE_LEN, peer_public_key, |
374 | 0 | X25519_PUBLIC_VALUE_LEN); |
375 | 0 | OPENSSL_memcpy(kem_context + 2 * X25519_PUBLIC_VALUE_LEN, key->public_key, |
376 | 0 | X25519_PUBLIC_VALUE_LEN); |
377 | 0 | if (!dhkem_extract_and_expand(key->kem->id, EVP_sha256(), out_shared_secret, |
378 | 0 | SHA256_DIGEST_LENGTH, dh, sizeof(dh), |
379 | 0 | kem_context, sizeof(kem_context))) { |
380 | 0 | return 0; |
381 | 0 | } |
382 | | |
383 | 0 | *out_enc_len = X25519_PUBLIC_VALUE_LEN; |
384 | 0 | *out_shared_secret_len = SHA256_DIGEST_LENGTH; |
385 | 0 | return 1; |
386 | 0 | } |
387 | | |
388 | | static int x25519_auth_decap(const EVP_HPKE_KEY *key, |
389 | | uint8_t *out_shared_secret, |
390 | | size_t *out_shared_secret_len, const uint8_t *enc, |
391 | | size_t enc_len, const uint8_t *peer_public_key, |
392 | 0 | size_t peer_public_key_len) { |
393 | 0 | uint8_t dh[2 * X25519_SHARED_KEY_LEN]; |
394 | 0 | if (enc_len != X25519_PUBLIC_VALUE_LEN || |
395 | 0 | peer_public_key_len != X25519_PUBLIC_VALUE_LEN || |
396 | 0 | !X25519(dh, key->private_key, enc) || |
397 | 0 | !X25519(dh + X25519_SHARED_KEY_LEN, key->private_key, peer_public_key)) { |
398 | 0 | OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_PEER_KEY); |
399 | 0 | return 0; |
400 | 0 | } |
401 | | |
402 | 0 | uint8_t kem_context[3 * X25519_PUBLIC_VALUE_LEN]; |
403 | 0 | OPENSSL_memcpy(kem_context, enc, X25519_PUBLIC_VALUE_LEN); |
404 | 0 | OPENSSL_memcpy(kem_context + X25519_PUBLIC_VALUE_LEN, key->public_key, |
405 | 0 | X25519_PUBLIC_VALUE_LEN); |
406 | 0 | OPENSSL_memcpy(kem_context + 2 * X25519_PUBLIC_VALUE_LEN, peer_public_key, |
407 | 0 | X25519_PUBLIC_VALUE_LEN); |
408 | 0 | if (!dhkem_extract_and_expand(key->kem->id, EVP_sha256(), out_shared_secret, |
409 | 0 | SHA256_DIGEST_LENGTH, dh, sizeof(dh), |
410 | 0 | kem_context, sizeof(kem_context))) { |
411 | 0 | return 0; |
412 | 0 | } |
413 | | |
414 | 0 | *out_shared_secret_len = SHA256_DIGEST_LENGTH; |
415 | 0 | return 1; |
416 | 0 | } |
417 | | |
418 | 13.6k | const EVP_HPKE_KEM *EVP_hpke_x25519_hkdf_sha256() { |
419 | 13.6k | static const EVP_HPKE_KEM kKEM = { |
420 | 13.6k | /*id=*/EVP_HPKE_DHKEM_X25519_HKDF_SHA256, |
421 | | // TODO(crbug.com/503758094): Expose this as an EVP_KEM. |
422 | 13.6k | /*evp_kem_func=*/nullptr, |
423 | 13.6k | /*key_method=*/nullptr, |
424 | 13.6k | /*public_key_len=*/X25519_PUBLIC_VALUE_LEN, |
425 | 13.6k | /*private_key_len=*/X25519_PRIVATE_KEY_LEN, |
426 | 13.6k | /*seed_len=*/X25519_PRIVATE_KEY_LEN, |
427 | 13.6k | /*enc_len=*/X25519_PUBLIC_VALUE_LEN, |
428 | 13.6k | x25519_init_key, |
429 | 13.6k | x25519_generate_key, |
430 | 13.6k | x25519_derive_key, |
431 | 13.6k | x25519_encap_with_seed, |
432 | 13.6k | x25519_decap, |
433 | 13.6k | x25519_auth_encap_with_seed, |
434 | 13.6k | x25519_auth_decap, |
435 | 13.6k | }; |
436 | 13.6k | return &kKEM; |
437 | 13.6k | } |
438 | | |
439 | 0 | #define P256_PRIVATE_KEY_LEN 32 |
440 | 0 | #define P256_PUBLIC_KEY_LEN 65 |
441 | 0 | #define P256_PUBLIC_VALUE_LEN 65 |
442 | 0 | #define P256_SEED_LEN 32 |
443 | 0 | #define P256_SHARED_KEY_LEN 32 |
444 | | |
445 | | static int p256_public_from_private(uint8_t out_pub[P256_PUBLIC_VALUE_LEN], |
446 | 0 | const uint8_t priv[P256_PRIVATE_KEY_LEN]) { |
447 | 0 | const EC_GROUP *const group = EC_group_p256(); |
448 | 0 | const uint8_t kAllZeros[P256_PRIVATE_KEY_LEN] = {0}; |
449 | 0 | EC_SCALAR private_scalar; |
450 | 0 | EC_JACOBIAN public_point; |
451 | 0 | EC_AFFINE public_point_affine; |
452 | |
|
453 | 0 | if (CRYPTO_memcmp(kAllZeros, priv, sizeof(kAllZeros)) == 0) { |
454 | 0 | OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR); |
455 | 0 | return 0; |
456 | 0 | } |
457 | | |
458 | 0 | if (!ec_scalar_from_bytes(group, &private_scalar, priv, |
459 | 0 | P256_PRIVATE_KEY_LEN) || |
460 | 0 | !ec_point_mul_scalar_base(group, &public_point, &private_scalar) || |
461 | 0 | !ec_jacobian_to_affine(group, &public_point_affine, &public_point)) { |
462 | 0 | return 0; |
463 | 0 | } |
464 | | |
465 | 0 | size_t out_len_x, out_len_y; |
466 | 0 | out_pub[0] = POINT_CONVERSION_UNCOMPRESSED; |
467 | 0 | ec_felem_to_bytes(group, &out_pub[1], &out_len_x, &public_point_affine.X); |
468 | 0 | ec_felem_to_bytes(group, &out_pub[33], &out_len_y, &public_point_affine.Y); |
469 | 0 | return 1; |
470 | 0 | } |
471 | | |
472 | | static int p256_init_key(EVP_HPKE_KEY *key, const uint8_t *priv_key, |
473 | 0 | size_t priv_key_len) { |
474 | 0 | if (priv_key_len != P256_PRIVATE_KEY_LEN) { |
475 | 0 | OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR); |
476 | 0 | return 0; |
477 | 0 | } |
478 | | |
479 | 0 | if (!p256_public_from_private(key->public_key, priv_key)) { |
480 | 0 | return 0; |
481 | 0 | } |
482 | | |
483 | 0 | OPENSSL_memcpy(key->private_key, priv_key, priv_key_len); |
484 | 0 | return 1; |
485 | 0 | } |
486 | | |
487 | | static int p256_private_key_from_seed(uint8_t out_priv[P256_PRIVATE_KEY_LEN], |
488 | 0 | const uint8_t *seed, size_t seed_len) { |
489 | | // https://www.rfc-editor.org/rfc/rfc9180.html#name-derivekeypair |
490 | 0 | auto suite_id = hpke_kem_suite_id(EVP_HPKE_DHKEM_P256_HKDF_SHA256); |
491 | 0 | uint8_t dkp_prk[32]; |
492 | 0 | size_t dkp_prk_len; |
493 | 0 | if (!hpke_labeled_extract(EVP_sha256(), dkp_prk, &dkp_prk_len, nullptr, 0, |
494 | 0 | suite_id.data(), suite_id.size(), "dkp_prk", seed, |
495 | 0 | seed_len)) { |
496 | 0 | return 0; |
497 | 0 | } |
498 | 0 | assert(dkp_prk_len == sizeof(dkp_prk)); |
499 | | |
500 | 0 | const EC_GROUP *const group = EC_group_p256(); |
501 | 0 | EC_SCALAR private_scalar; |
502 | |
|
503 | 0 | for (unsigned counter = 0; counter < 256; counter++) { |
504 | 0 | const uint8_t counter_byte = counter & 0xff; |
505 | 0 | if (!hpke_labeled_expand(EVP_sha256(), out_priv, P256_PRIVATE_KEY_LEN, |
506 | 0 | dkp_prk, sizeof(dkp_prk), suite_id.data(), |
507 | 0 | suite_id.size(), "candidate", &counter_byte, |
508 | 0 | sizeof(counter_byte))) { |
509 | 0 | return 0; |
510 | 0 | } |
511 | | |
512 | | // `ec_scalar_from_bytes` checks that the scalar is less than the order. |
513 | 0 | if (ec_scalar_from_bytes(group, &private_scalar, out_priv, |
514 | 0 | P256_PRIVATE_KEY_LEN) && |
515 | 0 | !ec_scalar_is_zero(group, &private_scalar)) { |
516 | 0 | return 1; |
517 | 0 | } |
518 | 0 | } |
519 | | |
520 | | // This happens with probability of 2^-(32*256). |
521 | 0 | OPENSSL_PUT_ERROR(EVP, ERR_R_INTERNAL_ERROR); |
522 | 0 | return 0; |
523 | 0 | } |
524 | | |
525 | 0 | static int p256_derive_key(EVP_HPKE_KEY *key, Span<const uint8_t> ikm) { |
526 | 0 | if (!p256_private_key_from_seed(key->private_key, ikm.data(), ikm.size()) || |
527 | 0 | !p256_public_from_private(key->public_key, key->private_key)) { |
528 | 0 | return 0; |
529 | 0 | } |
530 | 0 | return 1; |
531 | 0 | } |
532 | | |
533 | 0 | static int p256_generate_key(EVP_HPKE_KEY *key) { |
534 | 0 | uint8_t seed[P256_SEED_LEN]; |
535 | 0 | RAND_bytes(seed, sizeof(seed)); |
536 | 0 | return p256_derive_key(key, seed); |
537 | 0 | } |
538 | | |
539 | | static int p256(uint8_t out_dh[P256_SHARED_KEY_LEN], |
540 | | const uint8_t my_private[P256_PRIVATE_KEY_LEN], |
541 | 0 | const uint8_t their_public[P256_PUBLIC_VALUE_LEN]) { |
542 | 0 | const EC_GROUP *const group = EC_group_p256(); |
543 | 0 | EC_SCALAR private_scalar; |
544 | 0 | EC_FELEM x, y; |
545 | 0 | EC_JACOBIAN shared_point, their_point; |
546 | 0 | EC_AFFINE their_point_affine, shared_point_affine; |
547 | |
|
548 | 0 | if (their_public[0] != POINT_CONVERSION_UNCOMPRESSED || |
549 | 0 | !ec_felem_from_bytes(group, &x, &their_public[1], 32) || |
550 | 0 | !ec_felem_from_bytes(group, &y, &their_public[33], 32) || |
551 | 0 | !ec_point_set_affine_coordinates(group, &their_point_affine, &x, &y) || |
552 | 0 | !ec_scalar_from_bytes(group, &private_scalar, my_private, |
553 | 0 | P256_PRIVATE_KEY_LEN)) { |
554 | 0 | OPENSSL_PUT_ERROR(EVP, ERR_R_INTERNAL_ERROR); |
555 | 0 | return 0; |
556 | 0 | } |
557 | | |
558 | 0 | ec_affine_to_jacobian(group, &their_point, &their_point_affine); |
559 | 0 | if (!ec_point_mul_scalar(group, &shared_point, &their_point, |
560 | 0 | &private_scalar) || |
561 | 0 | !ec_jacobian_to_affine(group, &shared_point_affine, &shared_point)) { |
562 | 0 | OPENSSL_PUT_ERROR(EVP, ERR_R_INTERNAL_ERROR); |
563 | 0 | return 0; |
564 | 0 | } |
565 | | |
566 | 0 | size_t out_len; |
567 | 0 | ec_felem_to_bytes(group, out_dh, &out_len, &shared_point_affine.X); |
568 | 0 | assert(out_len == P256_SHARED_KEY_LEN); |
569 | 0 | return 1; |
570 | 0 | } |
571 | | |
572 | | static int p256_encap_with_seed(const EVP_HPKE_KEM *kem, |
573 | | uint8_t *out_shared_secret, |
574 | | size_t *out_shared_secret_len, uint8_t *out_enc, |
575 | | size_t *out_enc_len, size_t max_enc, |
576 | | const uint8_t *peer_public_key, |
577 | | size_t peer_public_key_len, const uint8_t *seed, |
578 | 0 | size_t seed_len) { |
579 | 0 | if (max_enc < P256_PUBLIC_VALUE_LEN) { |
580 | 0 | OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_BUFFER_SIZE); |
581 | 0 | return 0; |
582 | 0 | } |
583 | 0 | if (seed_len != P256_SEED_LEN) { |
584 | 0 | OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR); |
585 | 0 | return 0; |
586 | 0 | } |
587 | 0 | uint8_t private_key[P256_PRIVATE_KEY_LEN]; |
588 | 0 | if (!p256_private_key_from_seed(private_key, seed, seed_len) || |
589 | 0 | !p256_public_from_private(out_enc, private_key)) { |
590 | 0 | return 0; |
591 | 0 | } |
592 | | |
593 | 0 | uint8_t dh[P256_SHARED_KEY_LEN]; |
594 | 0 | if (peer_public_key_len != P256_PUBLIC_VALUE_LEN || |
595 | 0 | !p256(dh, private_key, peer_public_key)) { |
596 | 0 | OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_PEER_KEY); |
597 | 0 | return 0; |
598 | 0 | } |
599 | | |
600 | 0 | uint8_t kem_context[2 * P256_PUBLIC_VALUE_LEN]; |
601 | 0 | OPENSSL_memcpy(kem_context, out_enc, P256_PUBLIC_VALUE_LEN); |
602 | 0 | OPENSSL_memcpy(kem_context + P256_PUBLIC_VALUE_LEN, peer_public_key, |
603 | 0 | P256_PUBLIC_VALUE_LEN); |
604 | 0 | if (!dhkem_extract_and_expand(kem->id, EVP_sha256(), out_shared_secret, |
605 | 0 | SHA256_DIGEST_LENGTH, dh, sizeof(dh), |
606 | 0 | kem_context, sizeof(kem_context))) { |
607 | 0 | return 0; |
608 | 0 | } |
609 | | |
610 | 0 | *out_enc_len = P256_PUBLIC_VALUE_LEN; |
611 | 0 | *out_shared_secret_len = SHA256_DIGEST_LENGTH; |
612 | 0 | return 1; |
613 | 0 | } |
614 | | |
615 | | static int p256_decap(const EVP_HPKE_KEY *key, uint8_t *out_shared_secret, |
616 | | size_t *out_shared_secret_len, const uint8_t *enc, |
617 | 0 | size_t enc_len) { |
618 | 0 | uint8_t dh[P256_SHARED_KEY_LEN]; |
619 | 0 | if (enc_len != P256_PUBLIC_VALUE_LEN || // |
620 | 0 | !p256(dh, key->private_key, enc)) { |
621 | 0 | OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_PEER_KEY); |
622 | 0 | return 0; |
623 | 0 | } |
624 | | |
625 | 0 | uint8_t kem_context[2 * P256_PUBLIC_VALUE_LEN]; |
626 | 0 | OPENSSL_memcpy(kem_context, enc, P256_PUBLIC_VALUE_LEN); |
627 | 0 | OPENSSL_memcpy(kem_context + P256_PUBLIC_VALUE_LEN, key->public_key, |
628 | 0 | P256_PUBLIC_VALUE_LEN); |
629 | 0 | if (!dhkem_extract_and_expand(key->kem->id, EVP_sha256(), out_shared_secret, |
630 | 0 | SHA256_DIGEST_LENGTH, dh, sizeof(dh), |
631 | 0 | kem_context, sizeof(kem_context))) { |
632 | 0 | return 0; |
633 | 0 | } |
634 | | |
635 | 0 | *out_shared_secret_len = SHA256_DIGEST_LENGTH; |
636 | 0 | return 1; |
637 | 0 | } |
638 | | |
639 | | static int p256_auth_encap_with_seed( |
640 | | const EVP_HPKE_KEY *key, uint8_t *out_shared_secret, |
641 | | size_t *out_shared_secret_len, uint8_t *out_enc, size_t *out_enc_len, |
642 | | size_t max_enc, const uint8_t *peer_public_key, size_t peer_public_key_len, |
643 | 0 | const uint8_t *seed, size_t seed_len) { |
644 | 0 | if (max_enc < P256_PUBLIC_VALUE_LEN) { |
645 | 0 | OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_BUFFER_SIZE); |
646 | 0 | return 0; |
647 | 0 | } |
648 | 0 | if (seed_len != P256_SEED_LEN) { |
649 | 0 | OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR); |
650 | 0 | return 0; |
651 | 0 | } |
652 | 0 | uint8_t private_key[P256_PRIVATE_KEY_LEN]; |
653 | 0 | if (!p256_private_key_from_seed(private_key, seed, seed_len) || |
654 | 0 | !p256_public_from_private(out_enc, private_key)) { |
655 | 0 | return 0; |
656 | 0 | } |
657 | | |
658 | 0 | uint8_t dh[2 * P256_SHARED_KEY_LEN]; |
659 | 0 | if (peer_public_key_len != P256_PUBLIC_VALUE_LEN || |
660 | 0 | !p256(dh, private_key, peer_public_key) || |
661 | 0 | !p256(dh + P256_SHARED_KEY_LEN, key->private_key, peer_public_key)) { |
662 | 0 | OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_PEER_KEY); |
663 | 0 | return 0; |
664 | 0 | } |
665 | | |
666 | 0 | uint8_t kem_context[3 * P256_PUBLIC_VALUE_LEN]; |
667 | 0 | OPENSSL_memcpy(kem_context, out_enc, P256_PUBLIC_VALUE_LEN); |
668 | 0 | OPENSSL_memcpy(kem_context + P256_PUBLIC_VALUE_LEN, peer_public_key, |
669 | 0 | P256_PUBLIC_VALUE_LEN); |
670 | 0 | OPENSSL_memcpy(kem_context + 2 * P256_PUBLIC_VALUE_LEN, key->public_key, |
671 | 0 | P256_PUBLIC_VALUE_LEN); |
672 | 0 | if (!dhkem_extract_and_expand(key->kem->id, EVP_sha256(), out_shared_secret, |
673 | 0 | SHA256_DIGEST_LENGTH, dh, sizeof(dh), |
674 | 0 | kem_context, sizeof(kem_context))) { |
675 | 0 | return 0; |
676 | 0 | } |
677 | | |
678 | 0 | *out_enc_len = P256_PUBLIC_VALUE_LEN; |
679 | 0 | *out_shared_secret_len = SHA256_DIGEST_LENGTH; |
680 | 0 | return 1; |
681 | 0 | } |
682 | | |
683 | | static int p256_auth_decap(const EVP_HPKE_KEY *key, uint8_t *out_shared_secret, |
684 | | size_t *out_shared_secret_len, const uint8_t *enc, |
685 | | size_t enc_len, const uint8_t *peer_public_key, |
686 | 0 | size_t peer_public_key_len) { |
687 | 0 | uint8_t dh[2 * P256_SHARED_KEY_LEN]; |
688 | 0 | if (enc_len != P256_PUBLIC_VALUE_LEN || |
689 | 0 | peer_public_key_len != P256_PUBLIC_VALUE_LEN || |
690 | 0 | !p256(dh, key->private_key, enc) || |
691 | 0 | !p256(dh + P256_SHARED_KEY_LEN, key->private_key, peer_public_key)) { |
692 | 0 | OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_PEER_KEY); |
693 | 0 | return 0; |
694 | 0 | } |
695 | | |
696 | 0 | uint8_t kem_context[3 * P256_PUBLIC_VALUE_LEN]; |
697 | 0 | OPENSSL_memcpy(kem_context, enc, P256_PUBLIC_VALUE_LEN); |
698 | 0 | OPENSSL_memcpy(kem_context + P256_PUBLIC_VALUE_LEN, key->public_key, |
699 | 0 | P256_PUBLIC_VALUE_LEN); |
700 | 0 | OPENSSL_memcpy(kem_context + 2 * P256_PUBLIC_VALUE_LEN, peer_public_key, |
701 | 0 | P256_PUBLIC_VALUE_LEN); |
702 | 0 | if (!dhkem_extract_and_expand(key->kem->id, EVP_sha256(), out_shared_secret, |
703 | 0 | SHA256_DIGEST_LENGTH, dh, sizeof(dh), |
704 | 0 | kem_context, sizeof(kem_context))) { |
705 | 0 | return 0; |
706 | 0 | } |
707 | | |
708 | 0 | *out_shared_secret_len = SHA256_DIGEST_LENGTH; |
709 | 0 | return 1; |
710 | 0 | } |
711 | | |
712 | 0 | const EVP_HPKE_KEM *EVP_hpke_p256_hkdf_sha256() { |
713 | 0 | static const EVP_HPKE_KEM kKEM = { |
714 | 0 | /*id=*/EVP_HPKE_DHKEM_P256_HKDF_SHA256, |
715 | | // TODO(crbug.com/503758094): Expose this as an EVP_KEM. |
716 | 0 | /*evp_kem_func=*/nullptr, |
717 | 0 | /*key_method=*/nullptr, |
718 | 0 | /*public_key_len=*/P256_PUBLIC_KEY_LEN, |
719 | 0 | /*private_key_len=*/P256_PRIVATE_KEY_LEN, |
720 | 0 | /*seed_len=*/P256_SEED_LEN, |
721 | 0 | /*enc_len=*/P256_PUBLIC_VALUE_LEN, |
722 | 0 | p256_init_key, |
723 | 0 | p256_generate_key, |
724 | 0 | p256_derive_key, |
725 | 0 | p256_encap_with_seed, |
726 | 0 | p256_decap, |
727 | 0 | p256_auth_encap_with_seed, |
728 | 0 | p256_auth_decap, |
729 | 0 | }; |
730 | 0 | return &kKEM; |
731 | 0 | } |
732 | | |
733 | | #define XWING_PRIVATE_KEY_LEN XWING_PRIVATE_KEY_BYTES |
734 | | #define XWING_PUBLIC_KEY_LEN XWING_PUBLIC_KEY_BYTES |
735 | | #define XWING_PUBLIC_VALUE_LEN XWING_CIPHERTEXT_BYTES |
736 | | #define XWING_SEED_LEN 64 |
737 | | #define XWING_SHARED_KEY_LEN XWING_SHARED_SECRET_BYTES |
738 | | |
739 | | template <size_t SEED_BYTES> |
740 | | static EVP_PKEY *shake256_derive_key_pair_from_private_seed( |
741 | 0 | const EVP_PKEY_ALG *alg, uint16_t kem_suite_id, Span<const uint8_t> ikm) { |
742 | 0 | uint8_t seed[SEED_BYTES]; |
743 | 0 | hpke_shake256_labeled_derive(seed, ikm, hpke_kem_suite_id(kem_suite_id), |
744 | 0 | "DeriveKeyPair", /*context=*/{}); |
745 | 0 | return EVP_PKEY_from_private_seed(alg, seed, sizeof(seed)); |
746 | 0 | } Unexecuted instantiation: hpke.cc:evp_pkey_st* shake256_derive_key_pair_from_private_seed<32ul>(evp_pkey_alg_st const*, unsigned short, bssl::Span<unsigned char const, 18446744073709551615ul>) Unexecuted instantiation: hpke.cc:evp_pkey_st* shake256_derive_key_pair_from_private_seed<64ul>(evp_pkey_alg_st const*, unsigned short, bssl::Span<unsigned char const, 18446744073709551615ul>) |
747 | | |
748 | | static constexpr HpkeKeyMethod kXwingKeyMethod = { |
749 | | /*hpke_kem_id=*/EVP_HPKE_XWING, |
750 | | /*public_key_len=*/XWING_PUBLIC_KEY_LEN, |
751 | | /*private_key_len=*/XWING_PRIVATE_KEY_LEN, |
752 | | /*alg_func=*/&EVP_pkey_xwing, |
753 | | /*deserialize_private_key=*/&EVP_PKEY_from_private_seed, |
754 | | /*serialize_private_key=*/&EVP_PKEY_get_private_seed, |
755 | | /*deserialize_public_key=*/&EVP_PKEY_from_raw_public_key, |
756 | | /*serialize_public_key=*/&EVP_PKEY_get_raw_public_key, |
757 | | /*derive_key_pair=*/ |
758 | | shake256_derive_key_pair_from_private_seed<XWING_PRIVATE_KEY_LEN>, |
759 | | }; |
760 | | |
761 | 0 | const EVP_HPKE_KEM *EVP_hpke_xwing() { |
762 | 0 | static const EVP_HPKE_KEM kKEM = { |
763 | 0 | /*id=*/EVP_HPKE_XWING, |
764 | 0 | /*evp_kem_func=*/&EVP_kem_xwing, |
765 | 0 | /*key_method=*/&kXwingKeyMethod, |
766 | 0 | /*public_key_len=*/0, |
767 | 0 | /*private_key_len=*/0, |
768 | 0 | /*seed_len=*/0, |
769 | 0 | /*enc_len=*/0, |
770 | 0 | /*init_key=*/nullptr, |
771 | 0 | /*generate_key=*/nullptr, |
772 | 0 | /*derive_key=*/nullptr, |
773 | 0 | /*encap_with_seed=*/nullptr, |
774 | 0 | /*decap=*/nullptr, |
775 | | // X-Wing doesn't support authenticated encapsulation/decapsulation: |
776 | | // https://datatracker.ietf.org/doc/html/draft-connolly-cfrg-xwing-kem-08#name-use-in-hpke |
777 | 0 | /*auth_encap_with_seed=*/nullptr, |
778 | 0 | /*auth_decap=*/nullptr, |
779 | 0 | }; |
780 | 0 | return &kKEM; |
781 | 0 | } |
782 | | |
783 | | static constexpr HpkeKeyMethod kMlkem768KeyMethod = { |
784 | | /*hpke_kem_id=*/EVP_HPKE_MLKEM768, |
785 | | /*public_key_len=*/MLKEM768_PUBLIC_KEY_BYTES, |
786 | | /*private_key_len=*/MLKEM_SEED_BYTES, |
787 | | /*alg_func=*/&EVP_pkey_ml_kem_768, |
788 | | /*deserialize_private_key=*/&EVP_PKEY_from_private_seed, |
789 | | /*serialize_private_key=*/&EVP_PKEY_get_private_seed, |
790 | | /*deserialize_public_key=*/&EVP_PKEY_from_raw_public_key, |
791 | | /*serialize_public_key=*/&EVP_PKEY_get_raw_public_key, |
792 | | /*derive_key_pair=*/ |
793 | | shake256_derive_key_pair_from_private_seed<MLKEM_SEED_BYTES>, |
794 | | }; |
795 | | |
796 | 0 | const EVP_HPKE_KEM *EVP_hpke_mlkem768() { |
797 | 0 | static const EVP_HPKE_KEM kKEM = { |
798 | 0 | /*id=*/EVP_HPKE_MLKEM768, |
799 | 0 | /*evp_kem_func=*/&EVP_kem_ml_kem_768, |
800 | 0 | /*key_method=*/&kMlkem768KeyMethod, |
801 | 0 | /*public_key_len=*/0, |
802 | 0 | /*private_key_len=*/0, |
803 | 0 | /*seed_len=*/0, |
804 | 0 | /*enc_len=*/0, |
805 | 0 | /*init_key=*/nullptr, |
806 | 0 | /*generate_key=*/nullptr, |
807 | 0 | /*derive_key=*/nullptr, |
808 | 0 | /*encap_with_seed=*/nullptr, |
809 | 0 | /*decap=*/nullptr, |
810 | | // ML-KEM doesn't support authenticated encapsulation/decapsulation: |
811 | | // https://datatracker.ietf.org/doc/draft-ietf-hpke-pq/01/ |
812 | 0 | /*auth_encap_with_seed=*/nullptr, |
813 | 0 | /*auth_decap=*/nullptr, |
814 | 0 | }; |
815 | 0 | return &kKEM; |
816 | 0 | } |
817 | | |
818 | | static constexpr HpkeKeyMethod kMlkem1024KeyMethod = { |
819 | | /*hpke_kem_id=*/EVP_HPKE_MLKEM1024, |
820 | | /*public_key_len=*/MLKEM1024_PUBLIC_KEY_BYTES, |
821 | | /*private_key_len=*/MLKEM_SEED_BYTES, |
822 | | /*alg_func=*/&EVP_pkey_ml_kem_1024, |
823 | | /*deserialize_private_key=*/&EVP_PKEY_from_private_seed, |
824 | | /*serialize_private_key=*/&EVP_PKEY_get_private_seed, |
825 | | /*deserialize_public_key=*/&EVP_PKEY_from_raw_public_key, |
826 | | /*serialize_public_key=*/&EVP_PKEY_get_raw_public_key, |
827 | | /*derive_key_pair=*/ |
828 | | shake256_derive_key_pair_from_private_seed<MLKEM_SEED_BYTES>, |
829 | | }; |
830 | | |
831 | 0 | const EVP_HPKE_KEM *EVP_hpke_mlkem1024() { |
832 | 0 | static const EVP_HPKE_KEM kKEM = { |
833 | 0 | /*id=*/EVP_HPKE_MLKEM1024, |
834 | 0 | /*evp_kem_func=*/&EVP_kem_ml_kem_1024, |
835 | 0 | /*key_method=*/&kMlkem1024KeyMethod, |
836 | 0 | /*public_key_len=*/0, |
837 | 0 | /*private_key_len=*/0, |
838 | 0 | /*seed_len=*/0, |
839 | 0 | /*enc_len=*/0, |
840 | 0 | /*init_key=*/nullptr, |
841 | 0 | /*generate_key=*/nullptr, |
842 | 0 | /*derive_key=*/nullptr, |
843 | 0 | /*encap_with_seed=*/nullptr, |
844 | 0 | /*decap=*/nullptr, |
845 | | // ML-KEM doesn't support authenticated encapsulation/decapsulation: |
846 | | // https://datatracker.ietf.org/doc/draft-ietf-hpke-pq/01/ |
847 | 0 | /*auth_encap_with_seed=*/nullptr, |
848 | 0 | /*auth_decap=*/nullptr, |
849 | 0 | }; |
850 | 0 | return &kKEM; |
851 | 0 | } |
852 | | |
853 | | namespace { |
854 | | |
855 | | // Temporary helper to switch between EVP_KEM-based implementation and legacy |
856 | | // EVP_HPKE_KEM implementation. |
857 | | // TODO(crbug.com/535883377): Unify EVP_HPKE_KEM and EVP_KEM for all supported |
858 | | // HPKE KEMs. |
859 | | |
860 | 15.1k | bool uses_evp(const EVP_HPKE_KEM *kem) { |
861 | 15.1k | return kem->evp_kem_func != nullptr && kem->key_method != nullptr; |
862 | 15.1k | } |
863 | | |
864 | | } // namespace |
865 | | |
866 | 451 | uint16_t EVP_HPKE_KEM_id(const EVP_HPKE_KEM *kem) { return kem->id; } |
867 | | |
868 | 451 | size_t EVP_HPKE_KEM_public_key_len(const EVP_HPKE_KEM *kem) { |
869 | 451 | if (uses_evp(kem)) { |
870 | 0 | return kem->key_method->public_key_len; |
871 | 0 | } |
872 | 451 | return kem->public_key_len; |
873 | 451 | } |
874 | | |
875 | 0 | size_t EVP_HPKE_KEM_private_key_len(const EVP_HPKE_KEM *kem) { |
876 | 0 | if (uses_evp(kem)) { |
877 | 0 | return kem->key_method->private_key_len; |
878 | 0 | } |
879 | 0 | return kem->private_key_len; |
880 | 0 | } |
881 | | |
882 | | // TODO(crbug.com/535883377): Implement in terms of EVP_KEM for all supported |
883 | | // HPKE KEMs. |
884 | 0 | size_t EVP_HPKE_KEM_enc_len(const EVP_HPKE_KEM *kem) { |
885 | 0 | if (uses_evp(kem)) { |
886 | 0 | return EVP_KEM_ciphertext_len(kem->evp_kem_func()); |
887 | 0 | } |
888 | 0 | return kem->enc_len; |
889 | 0 | } |
890 | | |
891 | | // TODO(crbug.com/535883377): Implement in terms of EVP_KEM for all supported |
892 | | // HPKE KEMs. |
893 | 291 | size_t EVP_HPKE_KEM_shared_secret_len(const EVP_HPKE_KEM *kem) { |
894 | 291 | if (uses_evp(kem)) { |
895 | 0 | return EVP_KEM_secret_len(kem->evp_kem_func()); |
896 | 0 | } |
897 | | // All other KEMs have the same shared secret length. We assume we'll finish |
898 | | // https://crbug.com/503758094 and move all KEMs to EVP before adding one that |
899 | | // breaks this. |
900 | 291 | return SHA256_DIGEST_LENGTH; |
901 | 291 | } |
902 | | |
903 | | // TODO(crbug.com/535883377): Implement in terms of EVP_KEM for all supported |
904 | | // HPKE KEMs. |
905 | 40.0k | void EVP_HPKE_KEY_zero(EVP_HPKE_KEY *key) { |
906 | 40.0k | OPENSSL_memset(key, 0, sizeof(EVP_HPKE_KEY)); |
907 | 40.0k | } |
908 | | |
909 | | // TODO(crbug.com/535883377): Implement in terms of EVP_KEM for all supported |
910 | | // HPKE KEMs. |
911 | 26.4k | void EVP_HPKE_KEY_cleanup(EVP_HPKE_KEY *key) { |
912 | 26.4k | EVP_PKEY_free(key->pkey); |
913 | 26.4k | key->pkey = nullptr; |
914 | 26.4k | } |
915 | | |
916 | 0 | EVP_HPKE_KEY *EVP_HPKE_KEY_new() { |
917 | 0 | EVP_HPKE_KEY *key = New<EVP_HPKE_KEY>(); |
918 | 0 | if (key == nullptr) { |
919 | 0 | return nullptr; |
920 | 0 | } |
921 | 0 | EVP_HPKE_KEY_zero(key); |
922 | 0 | return key; |
923 | 0 | } |
924 | | |
925 | 0 | void EVP_HPKE_KEY_free(EVP_HPKE_KEY *key) { |
926 | 0 | if (key != nullptr) { |
927 | 0 | EVP_HPKE_KEY_cleanup(key); |
928 | 0 | Delete(key); |
929 | 0 | } |
930 | 0 | } |
931 | | |
932 | | // TODO(crbug.com/535883377): Implement in terms of EVP_KEM for all supported |
933 | | // HPKE KEMs. |
934 | 6 | int EVP_HPKE_KEY_copy(EVP_HPKE_KEY *dst, const EVP_HPKE_KEY *src) { |
935 | 6 | dst->kem = src->kem; |
936 | 6 | OPENSSL_memcpy(dst->private_key, src->private_key, sizeof(src->private_key)); |
937 | 6 | OPENSSL_memcpy(dst->public_key, src->public_key, sizeof(src->public_key)); |
938 | 6 | dst->pkey = EVP_PKEY_dup_ref(src->pkey); |
939 | 6 | return 1; |
940 | 6 | } |
941 | | |
942 | 0 | void EVP_HPKE_KEY_move(EVP_HPKE_KEY *out, EVP_HPKE_KEY *in) { |
943 | 0 | EVP_HPKE_KEY_cleanup(out); |
944 | | // For now, `EVP_HPKE_KEY` is trivially movable. |
945 | | // Note that Rust may move this structure. See |
946 | | // bssl-crypto/src/scoped.rs:EvpHpkeKey. |
947 | 0 | *out = *in; |
948 | 0 | EVP_HPKE_KEY_zero(in); |
949 | 0 | } |
950 | | |
951 | | int EVP_HPKE_KEY_init(EVP_HPKE_KEY *key, const EVP_HPKE_KEM *kem, |
952 | 13.6k | const uint8_t *priv_key, size_t priv_key_len) { |
953 | 13.6k | EVP_HPKE_KEY_zero(key); |
954 | 13.6k | key->kem = kem; |
955 | | // TODO(crbug.com/535883377): Implement in terms of EVP_KEM for all supported |
956 | | // HPKE KEMs. |
957 | 13.6k | if (uses_evp(kem)) { |
958 | 0 | UniquePtr<EVP_PKEY> pkey(kem->key_method->deserialize_private_key( |
959 | 0 | kem->key_method->alg_func(), priv_key, priv_key_len)); |
960 | 0 | if (pkey == nullptr) { |
961 | 0 | key->kem = nullptr; |
962 | 0 | return 0; |
963 | 0 | } |
964 | 0 | key->pkey = pkey.release(); |
965 | 0 | return 1; |
966 | 0 | } |
967 | 13.6k | if (!kem->init_key(key, priv_key, priv_key_len)) { |
968 | 802 | key->kem = nullptr; |
969 | 802 | return 0; |
970 | 802 | } |
971 | 12.8k | return 1; |
972 | 13.6k | } |
973 | | |
974 | 0 | int EVP_HPKE_KEY_generate(EVP_HPKE_KEY *key, const EVP_HPKE_KEM *kem) { |
975 | 0 | EVP_HPKE_KEY_zero(key); |
976 | 0 | key->kem = kem; |
977 | | // TODO(crbug.com/535883377): Implement in terms of EVP_KEM for all supported |
978 | | // HPKE KEMs. |
979 | 0 | if (uses_evp(kem)) { |
980 | 0 | UniquePtr<EVP_PKEY> pkey( |
981 | 0 | EVP_PKEY_generate_from_alg(kem->key_method->alg_func())); |
982 | 0 | if (pkey == nullptr) { |
983 | 0 | key->kem = nullptr; |
984 | 0 | return 0; |
985 | 0 | } |
986 | 0 | key->pkey = pkey.release(); |
987 | 0 | return 1; |
988 | 0 | } |
989 | 0 | if (!kem->generate_key(key)) { |
990 | 0 | key->kem = nullptr; |
991 | 0 | return 0; |
992 | 0 | } |
993 | 0 | return 1; |
994 | 0 | } |
995 | | |
996 | | int EVP_HPKE_KEY_derive(EVP_HPKE_KEY *key, const EVP_HPKE_KEM *kem, |
997 | 0 | const uint8_t *ikm, size_t ikm_len) { |
998 | 0 | EVP_HPKE_KEY_zero(key); |
999 | 0 | key->kem = kem; |
1000 | | // TODO(crbug.com/535883377): Implement in terms of EVP_KEM for all supported |
1001 | | // HPKE KEMs. |
1002 | 0 | if (uses_evp(kem)) { |
1003 | 0 | UniquePtr<EVP_PKEY> pkey(kem->key_method->derive_key_pair( |
1004 | 0 | kem->key_method->alg_func(), kem->id, Span(ikm, ikm_len))); |
1005 | 0 | if (pkey == nullptr) { |
1006 | 0 | key->kem = nullptr; |
1007 | 0 | return 0; |
1008 | 0 | } |
1009 | 0 | key->pkey = pkey.release(); |
1010 | 0 | return 1; |
1011 | 0 | } |
1012 | 0 | if (!kem->derive_key(key, Span(ikm, ikm_len))) { |
1013 | 0 | key->kem = nullptr; |
1014 | 0 | return 0; |
1015 | 0 | } |
1016 | 0 | return 1; |
1017 | 0 | } |
1018 | | |
1019 | 451 | const EVP_HPKE_KEM *EVP_HPKE_KEY_kem(const EVP_HPKE_KEY *key) { |
1020 | 451 | return key->kem; |
1021 | 451 | } |
1022 | | |
1023 | | int EVP_HPKE_KEY_public_key(const EVP_HPKE_KEY *key, uint8_t *out, |
1024 | 451 | size_t *out_len, size_t max_out) { |
1025 | 451 | if (max_out < EVP_HPKE_KEM_public_key_len(key->kem)) { |
1026 | 0 | OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_BUFFER_SIZE); |
1027 | 0 | return 0; |
1028 | 0 | } |
1029 | | // TODO(crbug.com/535883377): Implement in terms of EVP_KEM for all supported |
1030 | | // HPKE KEMs. |
1031 | 451 | if (uses_evp(key->kem)) { |
1032 | 0 | *out_len = max_out; |
1033 | 0 | return key->kem->key_method->serialize_public_key(key->pkey, out, out_len); |
1034 | 0 | } |
1035 | 451 | OPENSSL_memcpy(out, key->public_key, key->kem->public_key_len); |
1036 | 451 | *out_len = key->kem->public_key_len; |
1037 | 451 | return 1; |
1038 | 451 | } |
1039 | | |
1040 | | int EVP_HPKE_KEY_private_key(const EVP_HPKE_KEY *key, uint8_t *out, |
1041 | 0 | size_t *out_len, size_t max_out) { |
1042 | 0 | if (max_out < EVP_HPKE_KEM_private_key_len(key->kem)) { |
1043 | 0 | OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_BUFFER_SIZE); |
1044 | 0 | return 0; |
1045 | 0 | } |
1046 | | // TODO(crbug.com/535883377): Implement in terms of EVP_KEM for all supported |
1047 | | // HPKE KEMs. |
1048 | 0 | if (uses_evp(key->kem)) { |
1049 | 0 | *out_len = max_out; |
1050 | 0 | return key->kem->key_method->serialize_private_key(key->pkey, out, out_len); |
1051 | 0 | } |
1052 | 0 | OPENSSL_memcpy(out, key->private_key, key->kem->private_key_len); |
1053 | 0 | *out_len = key->kem->private_key_len; |
1054 | 0 | return 1; |
1055 | 0 | } |
1056 | | |
1057 | | |
1058 | | // Supported KDFs and AEADs. |
1059 | | |
1060 | 303 | const EVP_HPKE_KDF *EVP_hpke_hkdf_sha256() { |
1061 | 303 | static const EVP_HPKE_KDF kKDF = {EVP_HPKE_HKDF_SHA256, &EVP_sha256}; |
1062 | 303 | return &kKDF; |
1063 | 303 | } |
1064 | | |
1065 | 0 | const EVP_HPKE_KDF *EVP_hpke_hkdf_sha384() { |
1066 | 0 | static const EVP_HPKE_KDF kKDF = {EVP_HPKE_HKDF_SHA384, &EVP_sha384}; |
1067 | 0 | return &kKDF; |
1068 | 0 | } |
1069 | | |
1070 | 60 | uint16_t EVP_HPKE_KDF_id(const EVP_HPKE_KDF *kdf) { return kdf->id; } |
1071 | | |
1072 | 0 | const EVP_MD *EVP_HPKE_KDF_hkdf_md(const EVP_HPKE_KDF *kdf) { |
1073 | 0 | return kdf->hkdf_md_func(); |
1074 | 0 | } |
1075 | | |
1076 | 3.18k | const EVP_HPKE_AEAD *EVP_hpke_aes_128_gcm() { |
1077 | 3.18k | static const EVP_HPKE_AEAD kAEAD = {EVP_HPKE_AES_128_GCM, |
1078 | 3.18k | &EVP_aead_aes_128_gcm}; |
1079 | 3.18k | return &kAEAD; |
1080 | 3.18k | } |
1081 | | |
1082 | 2.43k | const EVP_HPKE_AEAD *EVP_hpke_aes_256_gcm() { |
1083 | 2.43k | static const EVP_HPKE_AEAD kAEAD = {EVP_HPKE_AES_256_GCM, |
1084 | 2.43k | &EVP_aead_aes_256_gcm}; |
1085 | 2.43k | return &kAEAD; |
1086 | 2.43k | } |
1087 | | |
1088 | 2.42k | const EVP_HPKE_AEAD *EVP_hpke_chacha20_poly1305() { |
1089 | 2.42k | static const EVP_HPKE_AEAD kAEAD = {EVP_HPKE_CHACHA20_POLY1305, |
1090 | 2.42k | &EVP_aead_chacha20_poly1305}; |
1091 | 2.42k | return &kAEAD; |
1092 | 2.42k | } |
1093 | | |
1094 | 8.08k | uint16_t EVP_HPKE_AEAD_id(const EVP_HPKE_AEAD *aead) { return aead->id; } |
1095 | | |
1096 | 291 | const EVP_AEAD *EVP_HPKE_AEAD_aead(const EVP_HPKE_AEAD *aead) { |
1097 | 291 | return aead->aead_func(); |
1098 | 291 | } |
1099 | | |
1100 | | |
1101 | | // HPKE implementation. |
1102 | | |
1103 | 291 | static std::array<uint8_t, 10> hpke_full_suite_id(const EVP_HPKE_CTX *ctx) { |
1104 | 291 | return { |
1105 | 291 | 'H', |
1106 | 291 | 'P', |
1107 | 291 | 'K', |
1108 | 291 | 'E', |
1109 | 291 | static_cast<uint8_t>(ctx->kem->id >> 8), |
1110 | 291 | static_cast<uint8_t>(ctx->kem->id), |
1111 | 291 | static_cast<uint8_t>(ctx->kdf->id >> 8), |
1112 | 291 | static_cast<uint8_t>(ctx->kdf->id), |
1113 | 291 | static_cast<uint8_t>(ctx->aead->id >> 8), |
1114 | 291 | static_cast<uint8_t>(ctx->aead->id), |
1115 | 291 | }; |
1116 | 291 | } |
1117 | | |
1118 | 291 | #define HPKE_MODE_BASE 0 |
1119 | 0 | #define HPKE_MODE_AUTH 2 |
1120 | | |
1121 | | static int hpke_key_schedule(EVP_HPKE_CTX *ctx, uint8_t mode, |
1122 | | const uint8_t *shared_secret, |
1123 | | size_t shared_secret_len, const uint8_t *info, |
1124 | 291 | size_t info_len) { |
1125 | 291 | auto suite_id = hpke_full_suite_id(ctx); |
1126 | | |
1127 | | // psk_id_hash = LabeledExtract("", "psk_id_hash", psk_id) |
1128 | | // TODO(davidben): Precompute this value and store it with the EVP_HPKE_KDF. |
1129 | 291 | const EVP_MD *hkdf_md = ctx->kdf->hkdf_md_func(); |
1130 | 291 | uint8_t psk_id_hash[EVP_MAX_MD_SIZE]; |
1131 | 291 | size_t psk_id_hash_len; |
1132 | 291 | if (!hpke_labeled_extract(hkdf_md, psk_id_hash, &psk_id_hash_len, nullptr, 0, |
1133 | 291 | suite_id.data(), suite_id.size(), "psk_id_hash", |
1134 | 291 | nullptr, 0)) { |
1135 | 0 | return 0; |
1136 | 0 | } |
1137 | | |
1138 | | // info_hash = LabeledExtract("", "info_hash", info) |
1139 | 291 | uint8_t info_hash[EVP_MAX_MD_SIZE]; |
1140 | 291 | size_t info_hash_len; |
1141 | 291 | if (!hpke_labeled_extract(hkdf_md, info_hash, &info_hash_len, nullptr, 0, |
1142 | 291 | suite_id.data(), suite_id.size(), "info_hash", info, |
1143 | 291 | info_len)) { |
1144 | 0 | return 0; |
1145 | 0 | } |
1146 | | |
1147 | | // key_schedule_context = concat(mode, psk_id_hash, info_hash) |
1148 | 291 | uint8_t context[sizeof(uint8_t) + 2 * EVP_MAX_MD_SIZE]; |
1149 | 291 | size_t context_len; |
1150 | 291 | CBB context_cbb; |
1151 | 291 | CBB_init_fixed(&context_cbb, context, sizeof(context)); |
1152 | 291 | if (!CBB_add_u8(&context_cbb, mode) || |
1153 | 291 | !CBB_add_bytes(&context_cbb, psk_id_hash, psk_id_hash_len) || |
1154 | 291 | !CBB_add_bytes(&context_cbb, info_hash, info_hash_len) || |
1155 | 291 | !CBB_finish(&context_cbb, nullptr, &context_len)) { |
1156 | 0 | return 0; |
1157 | 0 | } |
1158 | | |
1159 | | // secret = LabeledExtract(shared_secret, "secret", psk) |
1160 | 291 | uint8_t secret[EVP_MAX_MD_SIZE]; |
1161 | 291 | size_t secret_len; |
1162 | 291 | if (!hpke_labeled_extract(hkdf_md, secret, &secret_len, shared_secret, |
1163 | 291 | shared_secret_len, suite_id.data(), suite_id.size(), |
1164 | 291 | "secret", nullptr, 0)) { |
1165 | 0 | return 0; |
1166 | 0 | } |
1167 | | |
1168 | | // key = LabeledExpand(secret, "key", key_schedule_context, Nk) |
1169 | 291 | const EVP_AEAD *aead = EVP_HPKE_AEAD_aead(ctx->aead); |
1170 | 291 | uint8_t key[EVP_AEAD_MAX_KEY_LENGTH]; |
1171 | 291 | const size_t kKeyLen = EVP_AEAD_key_length(aead); |
1172 | 291 | if (!hpke_labeled_expand(hkdf_md, key, kKeyLen, secret, secret_len, |
1173 | 291 | suite_id.data(), suite_id.size(), "key", context, |
1174 | 291 | context_len) || |
1175 | 291 | !EVP_AEAD_CTX_init(&ctx->aead_ctx, aead, key, kKeyLen, |
1176 | 291 | EVP_AEAD_DEFAULT_TAG_LENGTH, nullptr)) { |
1177 | 0 | return 0; |
1178 | 0 | } |
1179 | | |
1180 | | // base_nonce = LabeledExpand(secret, "base_nonce", key_schedule_context, Nn) |
1181 | 291 | if (!hpke_labeled_expand(hkdf_md, ctx->base_nonce, |
1182 | 291 | EVP_AEAD_nonce_length(aead), secret, secret_len, |
1183 | 291 | suite_id.data(), suite_id.size(), "base_nonce", |
1184 | 291 | context, context_len)) { |
1185 | 0 | return 0; |
1186 | 0 | } |
1187 | | |
1188 | | // exporter_secret = LabeledExpand(secret, "exp", key_schedule_context, Nh) |
1189 | 291 | if (!hpke_labeled_expand(hkdf_md, ctx->exporter_secret, EVP_MD_size(hkdf_md), |
1190 | 291 | secret, secret_len, suite_id.data(), suite_id.size(), |
1191 | 291 | "exp", context, context_len)) { |
1192 | 0 | return 0; |
1193 | 0 | } |
1194 | | |
1195 | 291 | return 1; |
1196 | 291 | } |
1197 | | |
1198 | 98.2k | void EVP_HPKE_CTX_zero(EVP_HPKE_CTX *ctx) { |
1199 | 98.2k | OPENSSL_memset(ctx, 0, sizeof(EVP_HPKE_CTX)); |
1200 | 98.2k | EVP_AEAD_CTX_zero(&ctx->aead_ctx); |
1201 | 98.2k | } |
1202 | | |
1203 | 97.6k | void EVP_HPKE_CTX_cleanup(EVP_HPKE_CTX *ctx) { |
1204 | 97.6k | EVP_AEAD_CTX_cleanup(&ctx->aead_ctx); |
1205 | 97.6k | } |
1206 | | |
1207 | 0 | EVP_HPKE_CTX *EVP_HPKE_CTX_new() { |
1208 | 0 | EVP_HPKE_CTX *ctx = New<EVP_HPKE_CTX>(); |
1209 | 0 | if (ctx == nullptr) { |
1210 | 0 | return nullptr; |
1211 | 0 | } |
1212 | 0 | EVP_HPKE_CTX_zero(ctx); |
1213 | 0 | return ctx; |
1214 | 0 | } |
1215 | | |
1216 | 0 | void EVP_HPKE_CTX_free(EVP_HPKE_CTX *ctx) { |
1217 | 0 | if (ctx != nullptr) { |
1218 | 0 | EVP_HPKE_CTX_cleanup(ctx); |
1219 | 0 | Delete(ctx); |
1220 | 0 | } |
1221 | 0 | } |
1222 | | |
1223 | | int EVP_HPKE_CTX_setup_sender(EVP_HPKE_CTX *ctx, uint8_t *out_enc, |
1224 | | size_t *out_enc_len, size_t max_enc, |
1225 | | const EVP_HPKE_KEM *kem, const EVP_HPKE_KDF *kdf, |
1226 | | const EVP_HPKE_AEAD *aead, |
1227 | | const uint8_t *peer_public_key, |
1228 | | size_t peer_public_key_len, const uint8_t *info, |
1229 | 0 | size_t info_len) { |
1230 | | // TODO(crbug.com/535883377): Implement in terms of EVP_KEM for all supported |
1231 | | // HPKE KEMs. |
1232 | 0 | if (uses_evp(kem)) { |
1233 | 0 | EVP_HPKE_CTX_zero(ctx); |
1234 | 0 | ctx->is_sender = 1; |
1235 | 0 | ctx->kem = kem; |
1236 | 0 | ctx->kdf = kdf; |
1237 | 0 | ctx->aead = aead; |
1238 | 0 | uint8_t shared_secret[MAX_SHARED_SECRET_LEN]; |
1239 | 0 | size_t shared_secret_len; |
1240 | |
|
1241 | 0 | const EVP_KEM *evp_kem = kem->evp_kem_func(); |
1242 | 0 | if (max_enc < EVP_KEM_ciphertext_len(evp_kem)) { |
1243 | 0 | OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_BUFFER_SIZE); |
1244 | 0 | return 0; |
1245 | 0 | } |
1246 | 0 | *out_enc_len = EVP_KEM_ciphertext_len(evp_kem); |
1247 | 0 | shared_secret_len = EVP_KEM_secret_len(evp_kem); |
1248 | 0 | assert(size_t{MAX_SHARED_SECRET_LEN} >= shared_secret_len); |
1249 | | |
1250 | 0 | UniquePtr<EVP_PKEY> peer_pubkey(kem->key_method->deserialize_public_key( |
1251 | 0 | kem->key_method->alg_func(), peer_public_key, peer_public_key_len)); |
1252 | 0 | if (peer_pubkey == nullptr || |
1253 | 0 | !EVP_KEM_encap(evp_kem, out_enc, *out_enc_len, shared_secret, |
1254 | 0 | shared_secret_len, peer_pubkey.get()) || |
1255 | 0 | !hpke_key_schedule(ctx, HPKE_MODE_BASE, shared_secret, |
1256 | 0 | shared_secret_len, info, info_len)) { |
1257 | 0 | EVP_HPKE_CTX_cleanup(ctx); |
1258 | 0 | return 0; |
1259 | 0 | } |
1260 | 0 | return 1; |
1261 | 0 | } |
1262 | | |
1263 | 0 | uint8_t seed[MAX_SEED_LEN]; |
1264 | 0 | RAND_bytes(seed, kem->seed_len); |
1265 | 0 | return EVP_HPKE_CTX_setup_sender_with_seed_for_testing( |
1266 | 0 | ctx, out_enc, out_enc_len, max_enc, kem, kdf, aead, peer_public_key, |
1267 | 0 | peer_public_key_len, info, info_len, seed, kem->seed_len); |
1268 | 0 | } |
1269 | | |
1270 | | int EVP_HPKE_CTX_setup_sender_with_seed_for_testing( |
1271 | | EVP_HPKE_CTX *ctx, uint8_t *out_enc, size_t *out_enc_len, size_t max_enc, |
1272 | | const EVP_HPKE_KEM *kem, const EVP_HPKE_KDF *kdf, const EVP_HPKE_AEAD *aead, |
1273 | | const uint8_t *peer_public_key, size_t peer_public_key_len, |
1274 | | const uint8_t *info, size_t info_len, const uint8_t *seed, |
1275 | 0 | size_t seed_len) { |
1276 | 0 | EVP_HPKE_CTX_zero(ctx); |
1277 | 0 | ctx->is_sender = 1; |
1278 | 0 | ctx->kem = kem; |
1279 | 0 | ctx->kdf = kdf; |
1280 | 0 | ctx->aead = aead; |
1281 | 0 | uint8_t shared_secret[MAX_SHARED_SECRET_LEN]; |
1282 | 0 | size_t shared_secret_len; |
1283 | | |
1284 | | // TODO(crbug.com/535883377): Implement in terms of EVP_KEM for all supported |
1285 | | // HPKE KEMs. |
1286 | 0 | if (uses_evp(kem)) { |
1287 | 0 | const EVP_KEM *evp_kem = kem->evp_kem_func(); |
1288 | 0 | if (max_enc < EVP_KEM_ciphertext_len(evp_kem)) { |
1289 | 0 | OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_BUFFER_SIZE); |
1290 | 0 | return 0; |
1291 | 0 | } |
1292 | 0 | *out_enc_len = EVP_KEM_ciphertext_len(evp_kem); |
1293 | 0 | shared_secret_len = EVP_KEM_secret_len(evp_kem); |
1294 | 0 | assert(size_t{MAX_SHARED_SECRET_LEN} >= shared_secret_len); |
1295 | | |
1296 | 0 | UniquePtr<EVP_PKEY> peer_pubkey(kem->key_method->deserialize_public_key( |
1297 | 0 | kem->key_method->alg_func(), peer_public_key, peer_public_key_len)); |
1298 | 0 | if (peer_pubkey == nullptr || |
1299 | 0 | !EVP_KEM_encap_external_entropy_for_testing( |
1300 | 0 | evp_kem, out_enc, *out_enc_len, shared_secret, shared_secret_len, |
1301 | 0 | peer_pubkey.get(), seed, seed_len) || |
1302 | 0 | !hpke_key_schedule(ctx, HPKE_MODE_BASE, shared_secret, |
1303 | 0 | shared_secret_len, info, info_len)) { |
1304 | 0 | EVP_HPKE_CTX_cleanup(ctx); |
1305 | 0 | return 0; |
1306 | 0 | } |
1307 | 0 | return 1; |
1308 | 0 | } |
1309 | | |
1310 | 0 | if (!kem->encap_with_seed(kem, shared_secret, &shared_secret_len, out_enc, |
1311 | 0 | out_enc_len, max_enc, peer_public_key, |
1312 | 0 | peer_public_key_len, seed, seed_len) || |
1313 | 0 | !hpke_key_schedule(ctx, HPKE_MODE_BASE, shared_secret, shared_secret_len, |
1314 | 0 | info, info_len)) { |
1315 | 0 | EVP_HPKE_CTX_cleanup(ctx); |
1316 | 0 | return 0; |
1317 | 0 | } |
1318 | 0 | return 1; |
1319 | 0 | } |
1320 | | |
1321 | | int EVP_HPKE_CTX_setup_recipient_with_shared_secret( |
1322 | | EVP_HPKE_CTX *ctx, const EVP_HPKE_KEM *kem, const EVP_HPKE_KDF *kdf, |
1323 | | const EVP_HPKE_AEAD *aead, const uint8_t *shared_secret, |
1324 | 291 | size_t shared_secret_len, const uint8_t *info, size_t info_len) { |
1325 | 291 | EVP_HPKE_CTX_zero(ctx); |
1326 | | |
1327 | 291 | if (shared_secret_len != EVP_HPKE_KEM_shared_secret_len(kem)) { |
1328 | 0 | OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_BUFFER_SIZE); |
1329 | 0 | return 0; |
1330 | 0 | } |
1331 | | |
1332 | 291 | ctx->is_sender = 0; |
1333 | 291 | ctx->kem = kem; |
1334 | 291 | ctx->kdf = kdf; |
1335 | 291 | ctx->aead = aead; |
1336 | 291 | if (!hpke_key_schedule(ctx, HPKE_MODE_BASE, shared_secret, shared_secret_len, |
1337 | 291 | info, info_len)) { |
1338 | 0 | EVP_HPKE_CTX_cleanup(ctx); |
1339 | 0 | return 0; |
1340 | 0 | } |
1341 | 291 | return 1; |
1342 | 291 | } |
1343 | | |
1344 | | int EVP_HPKE_CTX_setup_recipient(EVP_HPKE_CTX *ctx, const EVP_HPKE_KEY *key, |
1345 | | const EVP_HPKE_KDF *kdf, |
1346 | | const EVP_HPKE_AEAD *aead, const uint8_t *enc, |
1347 | | size_t enc_len, const uint8_t *info, |
1348 | 303 | size_t info_len) { |
1349 | 303 | EVP_HPKE_CTX_zero(ctx); |
1350 | | |
1351 | 303 | uint8_t shared_secret[MAX_SHARED_SECRET_LEN]; |
1352 | 303 | size_t shared_secret_len; |
1353 | | |
1354 | | // TODO(crbug.com/535883377): Implement in terms of EVP_KEM for all supported |
1355 | | // HPKE KEMs. |
1356 | 303 | int decap_ok; |
1357 | 303 | if (uses_evp(key->kem)) { |
1358 | 0 | const EVP_KEM *evp_kem = key->kem->evp_kem_func(); |
1359 | 0 | const EVP_PKEY *decap_key = key->pkey; |
1360 | 0 | shared_secret_len = EVP_KEM_secret_len(evp_kem); |
1361 | 0 | assert(size_t{MAX_SHARED_SECRET_LEN} >= shared_secret_len); |
1362 | | |
1363 | 0 | decap_ok = EVP_KEM_decap(evp_kem, shared_secret, shared_secret_len, enc, |
1364 | 0 | enc_len, decap_key); |
1365 | 303 | } else { |
1366 | 303 | decap_ok = |
1367 | 303 | key->kem->decap(key, shared_secret, &shared_secret_len, enc, enc_len); |
1368 | 303 | } |
1369 | | |
1370 | 303 | return decap_ok && EVP_HPKE_CTX_setup_recipient_with_shared_secret( |
1371 | 291 | ctx, key->kem, kdf, aead, shared_secret, |
1372 | 291 | shared_secret_len, info, info_len); |
1373 | 303 | } |
1374 | | |
1375 | | |
1376 | | int EVP_HPKE_CTX_setup_auth_sender( |
1377 | | EVP_HPKE_CTX *ctx, uint8_t *out_enc, size_t *out_enc_len, size_t max_enc, |
1378 | | const EVP_HPKE_KEY *key, const EVP_HPKE_KDF *kdf, const EVP_HPKE_AEAD *aead, |
1379 | | const uint8_t *peer_public_key, size_t peer_public_key_len, |
1380 | 0 | const uint8_t *info, size_t info_len) { |
1381 | 0 | if (key->kem->auth_encap_with_seed == nullptr) { |
1382 | | // Not all HPKE KEMs support AuthEncap. |
1383 | 0 | OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE); |
1384 | 0 | return 0; |
1385 | 0 | } |
1386 | 0 | assert(!uses_evp(key->kem)); |
1387 | 0 | uint8_t seed[MAX_SEED_LEN]; |
1388 | 0 | RAND_bytes(seed, key->kem->seed_len); |
1389 | 0 | return EVP_HPKE_CTX_setup_auth_sender_with_seed_for_testing( |
1390 | 0 | ctx, out_enc, out_enc_len, max_enc, key, kdf, aead, peer_public_key, |
1391 | 0 | peer_public_key_len, info, info_len, seed, key->kem->seed_len); |
1392 | 0 | } |
1393 | | |
1394 | | int EVP_HPKE_CTX_setup_auth_sender_with_seed_for_testing( |
1395 | | EVP_HPKE_CTX *ctx, uint8_t *out_enc, size_t *out_enc_len, size_t max_enc, |
1396 | | const EVP_HPKE_KEY *key, const EVP_HPKE_KDF *kdf, const EVP_HPKE_AEAD *aead, |
1397 | | const uint8_t *peer_public_key, size_t peer_public_key_len, |
1398 | | const uint8_t *info, size_t info_len, const uint8_t *seed, |
1399 | 0 | size_t seed_len) { |
1400 | 0 | if (key->kem->auth_encap_with_seed == nullptr) { |
1401 | | // Not all HPKE KEMs support AuthEncap. |
1402 | 0 | OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE); |
1403 | 0 | return 0; |
1404 | 0 | } |
1405 | | |
1406 | 0 | EVP_HPKE_CTX_zero(ctx); |
1407 | 0 | ctx->is_sender = 1; |
1408 | 0 | ctx->kem = key->kem; |
1409 | 0 | ctx->kdf = kdf; |
1410 | 0 | ctx->aead = aead; |
1411 | 0 | uint8_t shared_secret[MAX_SHARED_SECRET_LEN]; |
1412 | 0 | size_t shared_secret_len; |
1413 | 0 | if (!key->kem->auth_encap_with_seed( |
1414 | 0 | key, shared_secret, &shared_secret_len, out_enc, out_enc_len, max_enc, |
1415 | 0 | peer_public_key, peer_public_key_len, seed, seed_len) || |
1416 | 0 | !hpke_key_schedule(ctx, HPKE_MODE_AUTH, shared_secret, shared_secret_len, |
1417 | 0 | info, info_len)) { |
1418 | 0 | EVP_HPKE_CTX_cleanup(ctx); |
1419 | 0 | return 0; |
1420 | 0 | } |
1421 | 0 | return 1; |
1422 | 0 | } |
1423 | | |
1424 | | int EVP_HPKE_CTX_setup_auth_recipient( |
1425 | | EVP_HPKE_CTX *ctx, const EVP_HPKE_KEY *key, const EVP_HPKE_KDF *kdf, |
1426 | | const EVP_HPKE_AEAD *aead, const uint8_t *enc, size_t enc_len, |
1427 | | const uint8_t *info, size_t info_len, const uint8_t *peer_public_key, |
1428 | 0 | size_t peer_public_key_len) { |
1429 | 0 | if (key->kem->auth_decap == nullptr) { |
1430 | | // Not all HPKE KEMs support AuthDecap. |
1431 | 0 | OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE); |
1432 | 0 | return 0; |
1433 | 0 | } |
1434 | | |
1435 | 0 | EVP_HPKE_CTX_zero(ctx); |
1436 | 0 | ctx->is_sender = 0; |
1437 | 0 | ctx->kem = key->kem; |
1438 | 0 | ctx->kdf = kdf; |
1439 | 0 | ctx->aead = aead; |
1440 | 0 | uint8_t shared_secret[MAX_SHARED_SECRET_LEN]; |
1441 | 0 | size_t shared_secret_len; |
1442 | 0 | if (!key->kem->auth_decap(key, shared_secret, &shared_secret_len, enc, |
1443 | 0 | enc_len, peer_public_key, peer_public_key_len) || |
1444 | 0 | !hpke_key_schedule(ctx, HPKE_MODE_AUTH, shared_secret, shared_secret_len, |
1445 | 0 | info, info_len)) { |
1446 | 0 | EVP_HPKE_CTX_cleanup(ctx); |
1447 | 0 | return 0; |
1448 | 0 | } |
1449 | 0 | return 1; |
1450 | 0 | } |
1451 | | |
1452 | | static void hpke_nonce(const EVP_HPKE_CTX *ctx, uint8_t *out_nonce, |
1453 | 30 | size_t nonce_len) { |
1454 | 30 | assert(nonce_len >= 8); |
1455 | | |
1456 | | // Write padded big-endian bytes of `ctx->seq` to `out_nonce`. |
1457 | 30 | OPENSSL_memset(out_nonce, 0, nonce_len); |
1458 | 30 | uint64_t seq_copy = ctx->seq; |
1459 | 270 | for (size_t i = 0; i < 8; i++) { |
1460 | 240 | out_nonce[nonce_len - i - 1] = seq_copy & 0xff; |
1461 | 240 | seq_copy >>= 8; |
1462 | 240 | } |
1463 | | |
1464 | | // XOR the encoded sequence with the `ctx->base_nonce`. |
1465 | 390 | for (size_t i = 0; i < nonce_len; i++) { |
1466 | 360 | out_nonce[i] ^= ctx->base_nonce[i]; |
1467 | 360 | } |
1468 | 30 | } |
1469 | | |
1470 | | int EVP_HPKE_CTX_open(EVP_HPKE_CTX *ctx, uint8_t *out, size_t *out_len, |
1471 | | size_t max_out_len, const uint8_t *in, size_t in_len, |
1472 | 30 | const uint8_t *ad, size_t ad_len) { |
1473 | 30 | if (ctx->is_sender) { |
1474 | 0 | OPENSSL_PUT_ERROR(EVP, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); |
1475 | 0 | return 0; |
1476 | 0 | } |
1477 | 30 | if (ctx->seq == UINT64_MAX) { |
1478 | 0 | OPENSSL_PUT_ERROR(EVP, ERR_R_OVERFLOW); |
1479 | 0 | return 0; |
1480 | 0 | } |
1481 | | |
1482 | 30 | uint8_t nonce[EVP_AEAD_MAX_NONCE_LENGTH]; |
1483 | 30 | const size_t nonce_len = EVP_AEAD_nonce_length(ctx->aead_ctx.aead); |
1484 | 30 | hpke_nonce(ctx, nonce, nonce_len); |
1485 | | |
1486 | 30 | if (!EVP_AEAD_CTX_open(&ctx->aead_ctx, out, out_len, max_out_len, nonce, |
1487 | 30 | nonce_len, in, in_len, ad, ad_len)) { |
1488 | 30 | return 0; |
1489 | 30 | } |
1490 | 0 | ctx->seq++; |
1491 | 0 | return 1; |
1492 | 30 | } |
1493 | | |
1494 | | int EVP_HPKE_CTX_seal(EVP_HPKE_CTX *ctx, uint8_t *out, size_t *out_len, |
1495 | | size_t max_out_len, const uint8_t *in, size_t in_len, |
1496 | 0 | const uint8_t *ad, size_t ad_len) { |
1497 | 0 | if (!ctx->is_sender) { |
1498 | 0 | OPENSSL_PUT_ERROR(EVP, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); |
1499 | 0 | return 0; |
1500 | 0 | } |
1501 | 0 | if (ctx->seq == UINT64_MAX) { |
1502 | 0 | OPENSSL_PUT_ERROR(EVP, ERR_R_OVERFLOW); |
1503 | 0 | return 0; |
1504 | 0 | } |
1505 | | |
1506 | 0 | uint8_t nonce[EVP_AEAD_MAX_NONCE_LENGTH]; |
1507 | 0 | const size_t nonce_len = EVP_AEAD_nonce_length(ctx->aead_ctx.aead); |
1508 | 0 | hpke_nonce(ctx, nonce, nonce_len); |
1509 | |
|
1510 | 0 | if (!EVP_AEAD_CTX_seal(&ctx->aead_ctx, out, out_len, max_out_len, nonce, |
1511 | 0 | nonce_len, in, in_len, ad, ad_len)) { |
1512 | 0 | return 0; |
1513 | 0 | } |
1514 | 0 | ctx->seq++; |
1515 | 0 | return 1; |
1516 | 0 | } |
1517 | | |
1518 | | int EVP_HPKE_CTX_export(const EVP_HPKE_CTX *ctx, uint8_t *out, |
1519 | | size_t secret_len, const uint8_t *context, |
1520 | 0 | size_t context_len) { |
1521 | 0 | auto suite_id = hpke_full_suite_id(ctx); |
1522 | 0 | const EVP_MD *hkdf_md = ctx->kdf->hkdf_md_func(); |
1523 | 0 | if (!hpke_labeled_expand(hkdf_md, out, secret_len, ctx->exporter_secret, |
1524 | 0 | EVP_MD_size(hkdf_md), suite_id.data(), |
1525 | 0 | suite_id.size(), "sec", context, context_len)) { |
1526 | 0 | return 0; |
1527 | 0 | } |
1528 | 0 | return 1; |
1529 | 0 | } |
1530 | | |
1531 | 0 | size_t EVP_HPKE_CTX_max_overhead(const EVP_HPKE_CTX *ctx) { |
1532 | 0 | assert(ctx->is_sender); |
1533 | 0 | return EVP_AEAD_max_overhead(EVP_AEAD_CTX_aead(&ctx->aead_ctx)); |
1534 | 0 | } |
1535 | | |
1536 | 0 | const EVP_HPKE_KEM *EVP_HPKE_CTX_kem(const EVP_HPKE_CTX *ctx) { |
1537 | 0 | return ctx->kem; |
1538 | 0 | } |
1539 | | |
1540 | 45 | const EVP_HPKE_AEAD *EVP_HPKE_CTX_aead(const EVP_HPKE_CTX *ctx) { |
1541 | 45 | return ctx->aead; |
1542 | 45 | } |
1543 | | |
1544 | 60 | const EVP_HPKE_KDF *EVP_HPKE_CTX_kdf(const EVP_HPKE_CTX *ctx) { |
1545 | 60 | return ctx->kdf; |
1546 | 60 | } |