• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright 2015 The BoringSSL Authors
2  *
3  * Permission to use, copy, modify, and/or distribute this software for any
4  * purpose with or without fee is hereby granted, provided that the above
5  * copyright notice and this permission notice appear in all copies.
6  *
7  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
14 
15 #include <openssl/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 #define OPENSSL_UNSTABLE_EXPERIMENTAL_KYBER
28 #include <openssl/experimental/kyber.h>
29 #include <openssl/hrss.h>
30 #include <openssl/mem.h>
31 #include <openssl/mlkem.h>
32 #include <openssl/nid.h>
33 #include <openssl/rand.h>
34 #include <openssl/span.h>
35 
36 #include "../crypto/internal.h"
37 #include "internal.h"
38 
39 BSSL_NAMESPACE_BEGIN
40 
41 namespace {
42 
43 class ECKeyShare : public SSLKeyShare {
44  public:
ECKeyShare(const EC_GROUP * group,uint16_t group_id)45   ECKeyShare(const EC_GROUP *group, uint16_t group_id)
46       : group_(group), group_id_(group_id) {}
47 
GroupID() const48   uint16_t GroupID() const override { return group_id_; }
49 
Generate(CBB * out)50   bool Generate(CBB *out) override {
51     assert(!private_key_);
52     // Generate a private key.
53     private_key_.reset(BN_new());
54     if (!private_key_ ||
55         !BN_rand_range_ex(private_key_.get(), 1, EC_GROUP_get0_order(group_))) {
56       return false;
57     }
58 
59     // Compute the corresponding public key and serialize it.
60     UniquePtr<EC_POINT> public_key(EC_POINT_new(group_));
61     if (!public_key ||
62         !EC_POINT_mul(group_, public_key.get(), private_key_.get(), nullptr,
63                       nullptr, /*ctx=*/nullptr) ||
64         !EC_POINT_point2cbb(out, group_, public_key.get(),
65                             POINT_CONVERSION_UNCOMPRESSED, /*ctx=*/nullptr)) {
66       return false;
67     }
68 
69     return true;
70   }
71 
Encap(CBB * out_ciphertext,Array<uint8_t> * out_secret,uint8_t * out_alert,Span<const uint8_t> peer_key)72   bool Encap(CBB *out_ciphertext, Array<uint8_t> *out_secret,
73              uint8_t *out_alert, Span<const uint8_t> peer_key) override {
74     // ECDH may be fit into a KEM-like abstraction by using a second keypair's
75     // public key as the ciphertext.
76     *out_alert = SSL_AD_INTERNAL_ERROR;
77     return Generate(out_ciphertext) && Decap(out_secret, out_alert, peer_key);
78   }
79 
Decap(Array<uint8_t> * out_secret,uint8_t * out_alert,Span<const uint8_t> ciphertext)80   bool Decap(Array<uint8_t> *out_secret, uint8_t *out_alert,
81              Span<const uint8_t> ciphertext) override {
82     assert(group_);
83     assert(private_key_);
84     *out_alert = SSL_AD_INTERNAL_ERROR;
85 
86     UniquePtr<EC_POINT> peer_point(EC_POINT_new(group_));
87     UniquePtr<EC_POINT> result(EC_POINT_new(group_));
88     UniquePtr<BIGNUM> x(BN_new());
89     if (!peer_point || !result || !x) {
90       return false;
91     }
92 
93     if (ciphertext.empty() || ciphertext[0] != POINT_CONVERSION_UNCOMPRESSED ||
94         !EC_POINT_oct2point(group_, peer_point.get(), ciphertext.data(),
95                             ciphertext.size(), /*ctx=*/nullptr)) {
96       OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT);
97       *out_alert = SSL_AD_ILLEGAL_PARAMETER;
98       return false;
99     }
100 
101     // Compute the x-coordinate of |peer_key| * |private_key_|.
102     if (!EC_POINT_mul(group_, result.get(), nullptr, peer_point.get(),
103                       private_key_.get(), /*ctx=*/nullptr) ||
104         !EC_POINT_get_affine_coordinates_GFp(group_, result.get(), x.get(),
105                                              nullptr, /*ctx=*/nullptr)) {
106       return false;
107     }
108 
109     // Encode the x-coordinate left-padded with zeros.
110     Array<uint8_t> secret;
111     if (!secret.InitForOverwrite((EC_GROUP_get_degree(group_) + 7) / 8) ||
112         !BN_bn2bin_padded(secret.data(), secret.size(), x.get())) {
113       return false;
114     }
115 
116     *out_secret = std::move(secret);
117     return true;
118   }
119 
SerializePrivateKey(CBB * out)120   bool SerializePrivateKey(CBB *out) override {
121     assert(group_);
122     assert(private_key_);
123     // Padding is added to avoid leaking the length.
124     size_t len = BN_num_bytes(EC_GROUP_get0_order(group_));
125     return BN_bn2cbb_padded(out, len, private_key_.get());
126   }
127 
DeserializePrivateKey(CBS * in)128   bool DeserializePrivateKey(CBS *in) override {
129     assert(!private_key_);
130     private_key_.reset(BN_bin2bn(CBS_data(in), CBS_len(in), nullptr));
131     return private_key_ != nullptr;
132   }
133 
134  private:
135   UniquePtr<BIGNUM> private_key_;
136   const EC_GROUP *const group_ = nullptr;
137   uint16_t group_id_;
138 };
139 
140 class X25519KeyShare : public SSLKeyShare {
141  public:
X25519KeyShare()142   X25519KeyShare() {}
143 
GroupID() const144   uint16_t GroupID() const override { return SSL_GROUP_X25519; }
145 
Generate(CBB * out)146   bool Generate(CBB *out) override {
147     uint8_t public_key[32];
148     X25519_keypair(public_key, private_key_);
149     return !!CBB_add_bytes(out, public_key, sizeof(public_key));
150   }
151 
Encap(CBB * out_ciphertext,Array<uint8_t> * out_secret,uint8_t * out_alert,Span<const uint8_t> peer_key)152   bool Encap(CBB *out_ciphertext, Array<uint8_t> *out_secret,
153              uint8_t *out_alert, Span<const uint8_t> peer_key) override {
154     // X25519 may be fit into a KEM-like abstraction by using a second keypair's
155     // public key as the ciphertext.
156     *out_alert = SSL_AD_INTERNAL_ERROR;
157     return Generate(out_ciphertext) && Decap(out_secret, out_alert, peer_key);
158   }
159 
Decap(Array<uint8_t> * out_secret,uint8_t * out_alert,Span<const uint8_t> ciphertext)160   bool Decap(Array<uint8_t> *out_secret, uint8_t *out_alert,
161              Span<const uint8_t> ciphertext) override {
162     *out_alert = SSL_AD_INTERNAL_ERROR;
163 
164     Array<uint8_t> secret;
165     if (!secret.InitForOverwrite(32)) {
166       return false;
167     }
168 
169     if (ciphertext.size() != 32 ||  //
170         !X25519(secret.data(), private_key_, ciphertext.data())) {
171       *out_alert = SSL_AD_ILLEGAL_PARAMETER;
172       OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT);
173       return false;
174     }
175 
176     *out_secret = std::move(secret);
177     return true;
178   }
179 
SerializePrivateKey(CBB * out)180   bool SerializePrivateKey(CBB *out) override {
181     return CBB_add_bytes(out, private_key_, sizeof(private_key_));
182   }
183 
DeserializePrivateKey(CBS * in)184   bool DeserializePrivateKey(CBS *in) override {
185     if (CBS_len(in) != sizeof(private_key_) ||
186         !CBS_copy_bytes(in, private_key_, sizeof(private_key_))) {
187       return false;
188     }
189     return true;
190   }
191 
192  private:
193   uint8_t private_key_[32];
194 };
195 
196 // draft-tls-westerbaan-xyber768d00-03
197 class X25519Kyber768KeyShare : public SSLKeyShare {
198  public:
X25519Kyber768KeyShare()199   X25519Kyber768KeyShare() {}
200 
GroupID() const201   uint16_t GroupID() const override {
202     return SSL_GROUP_X25519_KYBER768_DRAFT00;
203   }
204 
Generate(CBB * out)205   bool Generate(CBB *out) override {
206     uint8_t x25519_public_key[32];
207     X25519_keypair(x25519_public_key, x25519_private_key_);
208 
209     uint8_t kyber_public_key[KYBER_PUBLIC_KEY_BYTES];
210     KYBER_generate_key(kyber_public_key, &kyber_private_key_);
211 
212     if (!CBB_add_bytes(out, x25519_public_key, sizeof(x25519_public_key)) ||
213         !CBB_add_bytes(out, kyber_public_key, sizeof(kyber_public_key))) {
214       return false;
215     }
216 
217     return true;
218   }
219 
Encap(CBB * out_ciphertext,Array<uint8_t> * out_secret,uint8_t * out_alert,Span<const uint8_t> peer_key)220   bool Encap(CBB *out_ciphertext, Array<uint8_t> *out_secret,
221              uint8_t *out_alert, Span<const uint8_t> peer_key) override {
222     Array<uint8_t> secret;
223     if (!secret.InitForOverwrite(32 + KYBER_SHARED_SECRET_BYTES)) {
224       return false;
225     }
226 
227     uint8_t x25519_public_key[32];
228     X25519_keypair(x25519_public_key, x25519_private_key_);
229     KYBER_public_key peer_kyber_pub;
230     CBS peer_key_cbs, peer_x25519_cbs, peer_kyber_cbs;
231     CBS_init(&peer_key_cbs, peer_key.data(), peer_key.size());
232     if (!CBS_get_bytes(&peer_key_cbs, &peer_x25519_cbs, 32) ||
233         !CBS_get_bytes(&peer_key_cbs, &peer_kyber_cbs,
234                        KYBER_PUBLIC_KEY_BYTES) ||
235         CBS_len(&peer_key_cbs) != 0 ||
236         !X25519(secret.data(), x25519_private_key_,
237                 CBS_data(&peer_x25519_cbs)) ||
238         !KYBER_parse_public_key(&peer_kyber_pub, &peer_kyber_cbs)) {
239       *out_alert = SSL_AD_ILLEGAL_PARAMETER;
240       OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT);
241       return false;
242     }
243 
244     uint8_t kyber_ciphertext[KYBER_CIPHERTEXT_BYTES];
245     KYBER_encap(kyber_ciphertext, secret.data() + 32, &peer_kyber_pub);
246 
247     if (!CBB_add_bytes(out_ciphertext, x25519_public_key,
248                        sizeof(x25519_public_key)) ||
249         !CBB_add_bytes(out_ciphertext, kyber_ciphertext,
250                        sizeof(kyber_ciphertext))) {
251       return false;
252     }
253 
254     *out_secret = std::move(secret);
255     return true;
256   }
257 
Decap(Array<uint8_t> * out_secret,uint8_t * out_alert,Span<const uint8_t> ciphertext)258   bool Decap(Array<uint8_t> *out_secret, uint8_t *out_alert,
259              Span<const uint8_t> ciphertext) override {
260     *out_alert = SSL_AD_INTERNAL_ERROR;
261 
262     Array<uint8_t> secret;
263     if (!secret.InitForOverwrite(32 + KYBER_SHARED_SECRET_BYTES)) {
264       return false;
265     }
266 
267     if (ciphertext.size() != 32 + KYBER_CIPHERTEXT_BYTES ||
268         !X25519(secret.data(), x25519_private_key_, ciphertext.data())) {
269       *out_alert = SSL_AD_ILLEGAL_PARAMETER;
270       OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT);
271       return false;
272     }
273 
274     KYBER_decap(secret.data() + 32, ciphertext.data() + 32,
275                 &kyber_private_key_);
276     *out_secret = std::move(secret);
277     return true;
278   }
279 
280  private:
281   uint8_t x25519_private_key_[32];
282   KYBER_private_key kyber_private_key_;
283 };
284 
285 // draft-kwiatkowski-tls-ecdhe-mlkem-01
286 class X25519MLKEM768KeyShare : public SSLKeyShare {
287  public:
X25519MLKEM768KeyShare()288   X25519MLKEM768KeyShare() {}
289 
GroupID() const290   uint16_t GroupID() const override { return SSL_GROUP_X25519_MLKEM768; }
291 
Generate(CBB * out)292   bool Generate(CBB *out) override {
293     uint8_t mlkem_public_key[MLKEM768_PUBLIC_KEY_BYTES];
294     MLKEM768_generate_key(mlkem_public_key, /*optional_out_seed=*/nullptr,
295                           &mlkem_private_key_);
296 
297     uint8_t x25519_public_key[X25519_PUBLIC_VALUE_LEN];
298     X25519_keypair(x25519_public_key, x25519_private_key_);
299 
300     if (!CBB_add_bytes(out, mlkem_public_key, sizeof(mlkem_public_key)) ||
301         !CBB_add_bytes(out, x25519_public_key, sizeof(x25519_public_key))) {
302       return false;
303     }
304 
305     return true;
306   }
307 
Encap(CBB * out_ciphertext,Array<uint8_t> * out_secret,uint8_t * out_alert,Span<const uint8_t> peer_key)308   bool Encap(CBB *out_ciphertext, Array<uint8_t> *out_secret,
309              uint8_t *out_alert, Span<const uint8_t> peer_key) override {
310     Array<uint8_t> secret;
311     if (!secret.InitForOverwrite(MLKEM_SHARED_SECRET_BYTES +
312                                  X25519_SHARED_KEY_LEN)) {
313       return false;
314     }
315 
316     MLKEM768_public_key peer_mlkem_pub;
317     uint8_t x25519_public_key[X25519_PUBLIC_VALUE_LEN];
318     X25519_keypair(x25519_public_key, x25519_private_key_);
319     CBS peer_key_cbs, peer_mlkem_cbs, peer_x25519_cbs;
320     CBS_init(&peer_key_cbs, peer_key.data(), peer_key.size());
321     if (!CBS_get_bytes(&peer_key_cbs, &peer_mlkem_cbs,
322                        MLKEM768_PUBLIC_KEY_BYTES) ||
323         !MLKEM768_parse_public_key(&peer_mlkem_pub, &peer_mlkem_cbs) ||
324         !CBS_get_bytes(&peer_key_cbs, &peer_x25519_cbs,
325                        X25519_PUBLIC_VALUE_LEN) ||
326         CBS_len(&peer_key_cbs) != 0 ||
327         !X25519(secret.data() + MLKEM_SHARED_SECRET_BYTES, x25519_private_key_,
328                 CBS_data(&peer_x25519_cbs))) {
329       *out_alert = SSL_AD_ILLEGAL_PARAMETER;
330       OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT);
331       return false;
332     }
333 
334     uint8_t mlkem_ciphertext[MLKEM768_CIPHERTEXT_BYTES];
335     MLKEM768_encap(mlkem_ciphertext, secret.data(), &peer_mlkem_pub);
336 
337     if (!CBB_add_bytes(out_ciphertext, mlkem_ciphertext,
338                        sizeof(mlkem_ciphertext)) ||
339         !CBB_add_bytes(out_ciphertext, x25519_public_key,
340                        sizeof(x25519_public_key))) {
341       return false;
342     }
343 
344     *out_secret = std::move(secret);
345     return true;
346   }
347 
Decap(Array<uint8_t> * out_secret,uint8_t * out_alert,Span<const uint8_t> ciphertext)348   bool Decap(Array<uint8_t> *out_secret, uint8_t *out_alert,
349              Span<const uint8_t> ciphertext) override {
350     *out_alert = SSL_AD_INTERNAL_ERROR;
351 
352     Array<uint8_t> secret;
353     if (!secret.InitForOverwrite(MLKEM_SHARED_SECRET_BYTES +
354                                  X25519_SHARED_KEY_LEN)) {
355       return false;
356     }
357 
358     if (ciphertext.size() !=
359             MLKEM768_CIPHERTEXT_BYTES + X25519_PUBLIC_VALUE_LEN ||
360         !MLKEM768_decap(secret.data(), ciphertext.data(),
361                         MLKEM768_CIPHERTEXT_BYTES, &mlkem_private_key_) ||
362         !X25519(secret.data() + MLKEM_SHARED_SECRET_BYTES, x25519_private_key_,
363                 ciphertext.data() + MLKEM768_CIPHERTEXT_BYTES)) {
364       *out_alert = SSL_AD_ILLEGAL_PARAMETER;
365       OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT);
366       return false;
367     }
368 
369     *out_secret = std::move(secret);
370     return true;
371   }
372 
373  private:
374   uint8_t x25519_private_key_[32];
375   MLKEM768_private_key mlkem_private_key_;
376 };
377 
378 constexpr NamedGroup kNamedGroups[] = {
379     {NID_secp224r1, SSL_GROUP_SECP224R1, "P-224", "secp224r1"},
380     {NID_X9_62_prime256v1, SSL_GROUP_SECP256R1, "P-256", "prime256v1"},
381     {NID_secp384r1, SSL_GROUP_SECP384R1, "P-384", "secp384r1"},
382     {NID_secp521r1, SSL_GROUP_SECP521R1, "P-521", "secp521r1"},
383     {NID_X25519, SSL_GROUP_X25519, "X25519", "x25519"},
384     {NID_X25519Kyber768Draft00, SSL_GROUP_X25519_KYBER768_DRAFT00,
385      "X25519Kyber768Draft00", ""},
386     {NID_X25519MLKEM768, SSL_GROUP_X25519_MLKEM768, "X25519MLKEM768", ""},
387 };
388 
389 }  // namespace
390 
NamedGroups()391 Span<const NamedGroup> NamedGroups() { return kNamedGroups; }
392 
Create(uint16_t group_id)393 UniquePtr<SSLKeyShare> SSLKeyShare::Create(uint16_t group_id) {
394   switch (group_id) {
395     case SSL_GROUP_SECP224R1:
396       return MakeUnique<ECKeyShare>(EC_group_p224(), SSL_GROUP_SECP224R1);
397     case SSL_GROUP_SECP256R1:
398       return MakeUnique<ECKeyShare>(EC_group_p256(), SSL_GROUP_SECP256R1);
399     case SSL_GROUP_SECP384R1:
400       return MakeUnique<ECKeyShare>(EC_group_p384(), SSL_GROUP_SECP384R1);
401     case SSL_GROUP_SECP521R1:
402       return MakeUnique<ECKeyShare>(EC_group_p521(), SSL_GROUP_SECP521R1);
403     case SSL_GROUP_X25519:
404       return MakeUnique<X25519KeyShare>();
405     case SSL_GROUP_X25519_KYBER768_DRAFT00:
406       return MakeUnique<X25519Kyber768KeyShare>();
407     case SSL_GROUP_X25519_MLKEM768:
408       return MakeUnique<X25519MLKEM768KeyShare>();
409     default:
410       return nullptr;
411   }
412 }
413 
ssl_nid_to_group_id(uint16_t * out_group_id,int nid)414 bool ssl_nid_to_group_id(uint16_t *out_group_id, int nid) {
415   for (const auto &group : kNamedGroups) {
416     if (group.nid == nid) {
417       *out_group_id = group.group_id;
418       return true;
419     }
420   }
421   return false;
422 }
423 
ssl_name_to_group_id(uint16_t * out_group_id,const char * name,size_t len)424 bool ssl_name_to_group_id(uint16_t *out_group_id, const char *name,
425                           size_t len) {
426   for (const auto &group : kNamedGroups) {
427     if (len == strlen(group.name) &&  //
428         !strncmp(group.name, name, len)) {
429       *out_group_id = group.group_id;
430       return true;
431     }
432     if (strlen(group.alias) > 0 && len == strlen(group.alias) &&
433         !strncmp(group.alias, name, len)) {
434       *out_group_id = group.group_id;
435       return true;
436     }
437   }
438   return false;
439 }
440 
ssl_group_id_to_nid(uint16_t group_id)441 int ssl_group_id_to_nid(uint16_t group_id) {
442   for (const auto &group : kNamedGroups) {
443     if (group.group_id == group_id) {
444       return group.nid;
445     }
446   }
447   return NID_undef;
448 }
449 
450 BSSL_NAMESPACE_END
451 
452 using namespace bssl;
453 
SSL_get_group_name(uint16_t group_id)454 const char *SSL_get_group_name(uint16_t group_id) {
455   for (const auto &group : kNamedGroups) {
456     if (group.group_id == group_id) {
457       return group.name;
458     }
459   }
460   return nullptr;
461 }
462 
SSL_get_all_group_names(const char ** out,size_t max_out)463 size_t SSL_get_all_group_names(const char **out, size_t max_out) {
464   return GetAllNames(out, max_out, Span<const char *>(), &NamedGroup::name,
465                      Span(kNamedGroups));
466 }
467