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