1 // Copyright 2015 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/ssl.h>
16
17 #include <assert.h>
18 #include <string.h>
19
20 #include <utility>
21
22 #include <openssl/bn.h>
23 #include <openssl/bytestring.h>
24 #include <openssl/curve25519.h>
25 #include <openssl/ec.h>
26 #include <openssl/err.h>
27 #include <openssl/hrss.h>
28 #include <openssl/mem.h>
29 #include <openssl/mlkem.h>
30 #include <openssl/nid.h>
31 #include <openssl/rand.h>
32 #include <openssl/span.h>
33
34 #include "../crypto/internal.h"
35 #include "../crypto/kyber/internal.h"
36 #include "internal.h"
37
38 BSSL_NAMESPACE_BEGIN
39
40 namespace {
41
42 class ECKeyShare : public SSLKeyShare {
43 public:
ECKeyShare(const EC_GROUP * group,uint16_t group_id)44 ECKeyShare(const EC_GROUP *group, uint16_t group_id)
45 : group_(group), group_id_(group_id) {}
46
GroupID() const47 uint16_t GroupID() const override { return group_id_; }
48
Generate(CBB * out)49 bool Generate(CBB *out) override {
50 assert(!private_key_);
51 // Generate a private key.
52 private_key_.reset(BN_new());
53 if (!private_key_ ||
54 !BN_rand_range_ex(private_key_.get(), 1, EC_GROUP_get0_order(group_))) {
55 return false;
56 }
57
58 // Compute the corresponding public key and serialize it.
59 UniquePtr<EC_POINT> public_key(EC_POINT_new(group_));
60 if (!public_key ||
61 !EC_POINT_mul(group_, public_key.get(), private_key_.get(), nullptr,
62 nullptr, /*ctx=*/nullptr) ||
63 !EC_POINT_point2cbb(out, group_, public_key.get(),
64 POINT_CONVERSION_UNCOMPRESSED, /*ctx=*/nullptr)) {
65 return false;
66 }
67
68 return true;
69 }
70
Encap(CBB * out_ciphertext,Array<uint8_t> * out_secret,uint8_t * out_alert,Span<const uint8_t> peer_key)71 bool Encap(CBB *out_ciphertext, Array<uint8_t> *out_secret,
72 uint8_t *out_alert, Span<const uint8_t> peer_key) override {
73 // ECDH may be fit into a KEM-like abstraction by using a second keypair's
74 // public key as the ciphertext.
75 *out_alert = SSL_AD_INTERNAL_ERROR;
76 return Generate(out_ciphertext) && Decap(out_secret, out_alert, peer_key);
77 }
78
Decap(Array<uint8_t> * out_secret,uint8_t * out_alert,Span<const uint8_t> ciphertext)79 bool Decap(Array<uint8_t> *out_secret, uint8_t *out_alert,
80 Span<const uint8_t> ciphertext) override {
81 assert(group_);
82 assert(private_key_);
83 *out_alert = SSL_AD_INTERNAL_ERROR;
84
85 UniquePtr<EC_POINT> peer_point(EC_POINT_new(group_));
86 UniquePtr<EC_POINT> result(EC_POINT_new(group_));
87 UniquePtr<BIGNUM> x(BN_new());
88 if (!peer_point || !result || !x) {
89 return false;
90 }
91
92 if (ciphertext.empty() || ciphertext[0] != POINT_CONVERSION_UNCOMPRESSED ||
93 !EC_POINT_oct2point(group_, peer_point.get(), ciphertext.data(),
94 ciphertext.size(), /*ctx=*/nullptr)) {
95 OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT);
96 *out_alert = SSL_AD_ILLEGAL_PARAMETER;
97 return false;
98 }
99
100 // Compute the x-coordinate of |peer_key| * |private_key_|.
101 if (!EC_POINT_mul(group_, result.get(), nullptr, peer_point.get(),
102 private_key_.get(), /*ctx=*/nullptr) ||
103 !EC_POINT_get_affine_coordinates_GFp(group_, result.get(), x.get(),
104 nullptr, /*ctx=*/nullptr)) {
105 return false;
106 }
107
108 // Encode the x-coordinate left-padded with zeros.
109 Array<uint8_t> secret;
110 if (!secret.InitForOverwrite((EC_GROUP_get_degree(group_) + 7) / 8) ||
111 !BN_bn2bin_padded(secret.data(), secret.size(), x.get())) {
112 return false;
113 }
114
115 *out_secret = std::move(secret);
116 return true;
117 }
118
SerializePrivateKey(CBB * out)119 bool SerializePrivateKey(CBB *out) override {
120 assert(group_);
121 assert(private_key_);
122 // Padding is added to avoid leaking the length.
123 size_t len = BN_num_bytes(EC_GROUP_get0_order(group_));
124 return BN_bn2cbb_padded(out, len, private_key_.get());
125 }
126
DeserializePrivateKey(CBS * in)127 bool DeserializePrivateKey(CBS *in) override {
128 assert(!private_key_);
129 private_key_.reset(BN_bin2bn(CBS_data(in), CBS_len(in), nullptr));
130 return private_key_ != nullptr;
131 }
132
133 private:
134 UniquePtr<BIGNUM> private_key_;
135 const EC_GROUP *const group_ = nullptr;
136 uint16_t group_id_;
137 };
138
139 class X25519KeyShare : public SSLKeyShare {
140 public:
X25519KeyShare()141 X25519KeyShare() {}
142
GroupID() const143 uint16_t GroupID() const override { return SSL_GROUP_X25519; }
144
Generate(CBB * out)145 bool Generate(CBB *out) override {
146 uint8_t public_key[32];
147 X25519_keypair(public_key, private_key_);
148 return !!CBB_add_bytes(out, public_key, sizeof(public_key));
149 }
150
Encap(CBB * out_ciphertext,Array<uint8_t> * out_secret,uint8_t * out_alert,Span<const uint8_t> peer_key)151 bool Encap(CBB *out_ciphertext, Array<uint8_t> *out_secret,
152 uint8_t *out_alert, Span<const uint8_t> peer_key) override {
153 // X25519 may be fit into a KEM-like abstraction by using a second keypair's
154 // public key as the ciphertext.
155 *out_alert = SSL_AD_INTERNAL_ERROR;
156 return Generate(out_ciphertext) && Decap(out_secret, out_alert, peer_key);
157 }
158
Decap(Array<uint8_t> * out_secret,uint8_t * out_alert,Span<const uint8_t> ciphertext)159 bool Decap(Array<uint8_t> *out_secret, uint8_t *out_alert,
160 Span<const uint8_t> ciphertext) override {
161 *out_alert = SSL_AD_INTERNAL_ERROR;
162
163 Array<uint8_t> secret;
164 if (!secret.InitForOverwrite(32)) {
165 return false;
166 }
167
168 if (ciphertext.size() != 32 || //
169 !X25519(secret.data(), private_key_, ciphertext.data())) {
170 *out_alert = SSL_AD_ILLEGAL_PARAMETER;
171 OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT);
172 return false;
173 }
174
175 *out_secret = std::move(secret);
176 return true;
177 }
178
SerializePrivateKey(CBB * out)179 bool SerializePrivateKey(CBB *out) override {
180 return CBB_add_bytes(out, private_key_, sizeof(private_key_));
181 }
182
DeserializePrivateKey(CBS * in)183 bool DeserializePrivateKey(CBS *in) override {
184 if (CBS_len(in) != sizeof(private_key_) ||
185 !CBS_copy_bytes(in, private_key_, sizeof(private_key_))) {
186 return false;
187 }
188 return true;
189 }
190
191 private:
192 uint8_t private_key_[32];
193 };
194
195 // draft-tls-westerbaan-xyber768d00-03
196 class X25519Kyber768KeyShare : public SSLKeyShare {
197 public:
X25519Kyber768KeyShare()198 X25519Kyber768KeyShare() {}
199
GroupID() const200 uint16_t GroupID() const override {
201 return SSL_GROUP_X25519_KYBER768_DRAFT00;
202 }
203
Generate(CBB * out)204 bool Generate(CBB *out) override {
205 uint8_t x25519_public_key[32];
206 X25519_keypair(x25519_public_key, x25519_private_key_);
207
208 uint8_t kyber_public_key[KYBER_PUBLIC_KEY_BYTES];
209 KYBER_generate_key(kyber_public_key, &kyber_private_key_);
210
211 if (!CBB_add_bytes(out, x25519_public_key, sizeof(x25519_public_key)) ||
212 !CBB_add_bytes(out, kyber_public_key, sizeof(kyber_public_key))) {
213 return false;
214 }
215
216 return true;
217 }
218
Encap(CBB * out_ciphertext,Array<uint8_t> * out_secret,uint8_t * out_alert,Span<const uint8_t> peer_key)219 bool Encap(CBB *out_ciphertext, Array<uint8_t> *out_secret,
220 uint8_t *out_alert, Span<const uint8_t> peer_key) override {
221 Array<uint8_t> secret;
222 if (!secret.InitForOverwrite(32 + KYBER_SHARED_SECRET_BYTES)) {
223 return false;
224 }
225
226 uint8_t x25519_public_key[32];
227 X25519_keypair(x25519_public_key, x25519_private_key_);
228 KYBER_public_key peer_kyber_pub;
229 CBS peer_key_cbs, peer_x25519_cbs, peer_kyber_cbs;
230 CBS_init(&peer_key_cbs, peer_key.data(), peer_key.size());
231 if (!CBS_get_bytes(&peer_key_cbs, &peer_x25519_cbs, 32) ||
232 !CBS_get_bytes(&peer_key_cbs, &peer_kyber_cbs,
233 KYBER_PUBLIC_KEY_BYTES) ||
234 CBS_len(&peer_key_cbs) != 0 ||
235 !X25519(secret.data(), x25519_private_key_,
236 CBS_data(&peer_x25519_cbs)) ||
237 !KYBER_parse_public_key(&peer_kyber_pub, &peer_kyber_cbs)) {
238 *out_alert = SSL_AD_ILLEGAL_PARAMETER;
239 OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT);
240 return false;
241 }
242
243 uint8_t kyber_ciphertext[KYBER_CIPHERTEXT_BYTES];
244 KYBER_encap(kyber_ciphertext, secret.data() + 32, &peer_kyber_pub);
245
246 if (!CBB_add_bytes(out_ciphertext, x25519_public_key,
247 sizeof(x25519_public_key)) ||
248 !CBB_add_bytes(out_ciphertext, kyber_ciphertext,
249 sizeof(kyber_ciphertext))) {
250 return false;
251 }
252
253 *out_secret = std::move(secret);
254 return true;
255 }
256
Decap(Array<uint8_t> * out_secret,uint8_t * out_alert,Span<const uint8_t> ciphertext)257 bool Decap(Array<uint8_t> *out_secret, uint8_t *out_alert,
258 Span<const uint8_t> ciphertext) override {
259 *out_alert = SSL_AD_INTERNAL_ERROR;
260
261 Array<uint8_t> secret;
262 if (!secret.InitForOverwrite(32 + KYBER_SHARED_SECRET_BYTES)) {
263 return false;
264 }
265
266 if (ciphertext.size() != 32 + KYBER_CIPHERTEXT_BYTES ||
267 !X25519(secret.data(), x25519_private_key_, ciphertext.data())) {
268 *out_alert = SSL_AD_ILLEGAL_PARAMETER;
269 OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT);
270 return false;
271 }
272
273 KYBER_decap(secret.data() + 32, ciphertext.data() + 32,
274 &kyber_private_key_);
275 *out_secret = std::move(secret);
276 return true;
277 }
278
279 private:
280 uint8_t x25519_private_key_[32];
281 KYBER_private_key kyber_private_key_;
282 };
283
284 // draft-ietf-tls-ecdhe-mlkem-00
285 class X25519MLKEM768KeyShare : public SSLKeyShare {
286 public:
X25519MLKEM768KeyShare()287 X25519MLKEM768KeyShare() {}
288
GroupID() const289 uint16_t GroupID() const override { return SSL_GROUP_X25519_MLKEM768; }
290
Generate(CBB * out)291 bool Generate(CBB *out) override {
292 uint8_t mlkem_public_key[MLKEM768_PUBLIC_KEY_BYTES];
293 MLKEM768_generate_key(mlkem_public_key, /*optional_out_seed=*/nullptr,
294 &mlkem_private_key_);
295
296 uint8_t x25519_public_key[X25519_PUBLIC_VALUE_LEN];
297 X25519_keypair(x25519_public_key, x25519_private_key_);
298
299 if (!CBB_add_bytes(out, mlkem_public_key, sizeof(mlkem_public_key)) ||
300 !CBB_add_bytes(out, x25519_public_key, sizeof(x25519_public_key))) {
301 return false;
302 }
303
304 return true;
305 }
306
Encap(CBB * out_ciphertext,Array<uint8_t> * out_secret,uint8_t * out_alert,Span<const uint8_t> peer_key)307 bool Encap(CBB *out_ciphertext, Array<uint8_t> *out_secret,
308 uint8_t *out_alert, Span<const uint8_t> peer_key) override {
309 Array<uint8_t> secret;
310 if (!secret.InitForOverwrite(MLKEM_SHARED_SECRET_BYTES +
311 X25519_SHARED_KEY_LEN)) {
312 return false;
313 }
314
315 MLKEM768_public_key peer_mlkem_pub;
316 uint8_t x25519_public_key[X25519_PUBLIC_VALUE_LEN];
317 X25519_keypair(x25519_public_key, x25519_private_key_);
318 CBS peer_key_cbs, peer_mlkem_cbs, peer_x25519_cbs;
319 CBS_init(&peer_key_cbs, peer_key.data(), peer_key.size());
320 if (!CBS_get_bytes(&peer_key_cbs, &peer_mlkem_cbs,
321 MLKEM768_PUBLIC_KEY_BYTES) ||
322 !MLKEM768_parse_public_key(&peer_mlkem_pub, &peer_mlkem_cbs) ||
323 !CBS_get_bytes(&peer_key_cbs, &peer_x25519_cbs,
324 X25519_PUBLIC_VALUE_LEN) ||
325 CBS_len(&peer_key_cbs) != 0 ||
326 !X25519(secret.data() + MLKEM_SHARED_SECRET_BYTES, x25519_private_key_,
327 CBS_data(&peer_x25519_cbs))) {
328 *out_alert = SSL_AD_ILLEGAL_PARAMETER;
329 OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT);
330 return false;
331 }
332
333 uint8_t mlkem_ciphertext[MLKEM768_CIPHERTEXT_BYTES];
334 MLKEM768_encap(mlkem_ciphertext, secret.data(), &peer_mlkem_pub);
335
336 if (!CBB_add_bytes(out_ciphertext, mlkem_ciphertext,
337 sizeof(mlkem_ciphertext)) ||
338 !CBB_add_bytes(out_ciphertext, x25519_public_key,
339 sizeof(x25519_public_key))) {
340 return false;
341 }
342
343 *out_secret = std::move(secret);
344 return true;
345 }
346
Decap(Array<uint8_t> * out_secret,uint8_t * out_alert,Span<const uint8_t> ciphertext)347 bool Decap(Array<uint8_t> *out_secret, uint8_t *out_alert,
348 Span<const uint8_t> ciphertext) override {
349 *out_alert = SSL_AD_INTERNAL_ERROR;
350
351 Array<uint8_t> secret;
352 if (!secret.InitForOverwrite(MLKEM_SHARED_SECRET_BYTES +
353 X25519_SHARED_KEY_LEN)) {
354 return false;
355 }
356
357 if (ciphertext.size() !=
358 MLKEM768_CIPHERTEXT_BYTES + X25519_PUBLIC_VALUE_LEN ||
359 !MLKEM768_decap(secret.data(), ciphertext.data(),
360 MLKEM768_CIPHERTEXT_BYTES, &mlkem_private_key_) ||
361 !X25519(secret.data() + MLKEM_SHARED_SECRET_BYTES, x25519_private_key_,
362 ciphertext.data() + MLKEM768_CIPHERTEXT_BYTES)) {
363 *out_alert = SSL_AD_ILLEGAL_PARAMETER;
364 OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT);
365 return false;
366 }
367
368 *out_secret = std::move(secret);
369 return true;
370 }
371
372 private:
373 uint8_t x25519_private_key_[32];
374 MLKEM768_private_key mlkem_private_key_;
375 };
376
377 // draft-ietf-tls-mlkem-04
378 class MLKEM1024KeyShare : public SSLKeyShare {
379 public:
380 MLKEM1024KeyShare() = default;
381
GroupID() const382 uint16_t GroupID() const override { return SSL_GROUP_MLKEM1024; }
383
Generate(CBB * out_public_key)384 bool Generate(CBB *out_public_key) override {
385 uint8_t public_key[MLKEM1024_PUBLIC_KEY_BYTES];
386 MLKEM1024_generate_key(public_key, /*optional_out_seed=*/nullptr,
387 &private_key_);
388 return CBB_add_bytes(out_public_key, public_key, sizeof(public_key));
389 }
390
Encap(CBB * out_ciphertext,Array<uint8_t> * out_secret,uint8_t * out_alert,Span<const uint8_t> peer_key)391 bool Encap(CBB *out_ciphertext, Array<uint8_t> *out_secret,
392 uint8_t *out_alert, Span<const uint8_t> peer_key) override {
393 MLKEM1024_public_key peer_pub;
394 CBS peer_pub_cbs;
395 CBS_init(&peer_pub_cbs, peer_key.data(), peer_key.size());
396 if (!MLKEM1024_parse_public_key(&peer_pub, &peer_pub_cbs)) {
397 *out_alert = SSL_AD_ILLEGAL_PARAMETER;
398 OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT);
399 return false;
400 }
401
402 Array<uint8_t> secret;
403 if (!secret.InitForOverwrite(MLKEM_SHARED_SECRET_BYTES)) {
404 return false;
405 }
406 uint8_t ciphertext[MLKEM1024_CIPHERTEXT_BYTES];
407 MLKEM1024_encap(ciphertext, secret.data(), &peer_pub);
408 if (!CBB_add_bytes(out_ciphertext, ciphertext, sizeof(ciphertext))) {
409 return false;
410 }
411 *out_secret = std::move(secret);
412 return true;
413 }
414
Decap(Array<uint8_t> * out_secret,uint8_t * out_alert,Span<const uint8_t> ciphertext)415 bool Decap(Array<uint8_t> *out_secret, uint8_t *out_alert,
416 Span<const uint8_t> ciphertext) override {
417 Array<uint8_t> secret;
418 if (!secret.InitForOverwrite(MLKEM_SHARED_SECRET_BYTES)) {
419 *out_alert = SSL_AD_INTERNAL_ERROR;
420 return false;
421 }
422
423 if (!MLKEM1024_decap(secret.data(), ciphertext.data(), ciphertext.size(),
424 &private_key_)) {
425 *out_alert = SSL_AD_ILLEGAL_PARAMETER;
426 OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT);
427 return false;
428 }
429 *out_secret = std::move(secret);
430 return true;
431 }
432
433 private:
434 MLKEM1024_private_key private_key_;
435 };
436
437 constexpr NamedGroup kNamedGroups[] = {
438 {NID_X9_62_prime256v1, SSL_GROUP_SECP256R1, "P-256", "prime256v1"},
439 {NID_secp384r1, SSL_GROUP_SECP384R1, "P-384", "secp384r1"},
440 {NID_secp521r1, SSL_GROUP_SECP521R1, "P-521", "secp521r1"},
441 {NID_X25519, SSL_GROUP_X25519, "X25519", "x25519"},
442 {NID_X25519Kyber768Draft00, SSL_GROUP_X25519_KYBER768_DRAFT00,
443 "X25519Kyber768Draft00", ""},
444 {NID_X25519MLKEM768, SSL_GROUP_X25519_MLKEM768, "X25519MLKEM768", ""},
445 {NID_MLKEM1024, SSL_GROUP_MLKEM1024, "MLKEM1024", ""},
446 };
447
448 } // namespace
449
NamedGroups()450 Span<const NamedGroup> NamedGroups() { return kNamedGroups; }
451
Create(uint16_t group_id)452 UniquePtr<SSLKeyShare> SSLKeyShare::Create(uint16_t group_id) {
453 switch (group_id) {
454 case SSL_GROUP_SECP256R1:
455 return MakeUnique<ECKeyShare>(EC_group_p256(), SSL_GROUP_SECP256R1);
456 case SSL_GROUP_SECP384R1:
457 return MakeUnique<ECKeyShare>(EC_group_p384(), SSL_GROUP_SECP384R1);
458 case SSL_GROUP_SECP521R1:
459 return MakeUnique<ECKeyShare>(EC_group_p521(), SSL_GROUP_SECP521R1);
460 case SSL_GROUP_X25519:
461 return MakeUnique<X25519KeyShare>();
462 case SSL_GROUP_X25519_KYBER768_DRAFT00:
463 return MakeUnique<X25519Kyber768KeyShare>();
464 case SSL_GROUP_X25519_MLKEM768:
465 return MakeUnique<X25519MLKEM768KeyShare>();
466 case SSL_GROUP_MLKEM1024:
467 return MakeUnique<MLKEM1024KeyShare>();
468 default:
469 return nullptr;
470 }
471 }
472
ssl_nid_to_group_id(uint16_t * out_group_id,int nid)473 bool ssl_nid_to_group_id(uint16_t *out_group_id, int nid) {
474 for (const auto &group : kNamedGroups) {
475 if (group.nid == nid) {
476 *out_group_id = group.group_id;
477 return true;
478 }
479 }
480 return false;
481 }
482
ssl_name_to_group_id(uint16_t * out_group_id,const char * name,size_t len)483 bool ssl_name_to_group_id(uint16_t *out_group_id, const char *name,
484 size_t len) {
485 for (const auto &group : kNamedGroups) {
486 if (len == strlen(group.name) && //
487 !strncmp(group.name, name, len)) {
488 *out_group_id = group.group_id;
489 return true;
490 }
491 if (strlen(group.alias) > 0 && len == strlen(group.alias) &&
492 !strncmp(group.alias, name, len)) {
493 *out_group_id = group.group_id;
494 return true;
495 }
496 }
497 return false;
498 }
499
ssl_group_id_to_nid(uint16_t group_id)500 int ssl_group_id_to_nid(uint16_t group_id) {
501 for (const auto &group : kNamedGroups) {
502 if (group.group_id == group_id) {
503 return group.nid;
504 }
505 }
506 return NID_undef;
507 }
508
509 BSSL_NAMESPACE_END
510
511 using namespace bssl;
512
SSL_get_group_name(uint16_t group_id)513 const char *SSL_get_group_name(uint16_t group_id) {
514 for (const auto &group : kNamedGroups) {
515 if (group.group_id == group_id) {
516 return group.name;
517 }
518 }
519 return nullptr;
520 }
521
SSL_get_all_group_names(const char ** out,size_t max_out)522 size_t SSL_get_all_group_names(const char **out, size_t max_out) {
523 return GetAllNames(out, max_out, Span<const char *>(), &NamedGroup::name,
524 Span(kNamedGroups));
525 }
526