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