/src/boringssl/ssl/ssl_key_share.cc
Line | Count | Source (jump to first uncovered line) |
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 | | #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: |
45 | | ECKeyShare(const EC_GROUP *group, uint16_t group_id) |
46 | 32.0k | : group_(group), group_id_(group_id) {} |
47 | | |
48 | 18 | uint16_t GroupID() const override { return group_id_; } |
49 | | |
50 | 31.5k | bool Generate(CBB *out) override { |
51 | 31.5k | assert(!private_key_); |
52 | | // Generate a private key. |
53 | 31.5k | private_key_.reset(BN_new()); |
54 | 31.5k | if (!private_key_ || |
55 | 31.5k | !BN_rand_range_ex(private_key_.get(), 1, EC_GROUP_get0_order(group_))) { |
56 | 0 | return false; |
57 | 0 | } |
58 | | |
59 | | // Compute the corresponding public key and serialize it. |
60 | 31.5k | UniquePtr<EC_POINT> public_key(EC_POINT_new(group_)); |
61 | 31.5k | if (!public_key || |
62 | 31.5k | !EC_POINT_mul(group_, public_key.get(), private_key_.get(), nullptr, |
63 | 31.5k | nullptr, /*ctx=*/nullptr) || |
64 | 31.5k | !EC_POINT_point2cbb(out, group_, public_key.get(), |
65 | 31.5k | POINT_CONVERSION_UNCOMPRESSED, /*ctx=*/nullptr)) { |
66 | 0 | return false; |
67 | 0 | } |
68 | | |
69 | 31.5k | return true; |
70 | 31.5k | } |
71 | | |
72 | | bool Encap(CBB *out_ciphertext, Array<uint8_t> *out_secret, |
73 | 30.0k | 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 | 30.0k | *out_alert = SSL_AD_INTERNAL_ERROR; |
77 | 30.0k | return Generate(out_ciphertext) && Decap(out_secret, out_alert, peer_key); |
78 | 30.0k | } |
79 | | |
80 | | bool Decap(Array<uint8_t> *out_secret, uint8_t *out_alert, |
81 | 30.7k | Span<const uint8_t> ciphertext) override { |
82 | 30.7k | assert(group_); |
83 | 30.7k | assert(private_key_); |
84 | 30.7k | *out_alert = SSL_AD_INTERNAL_ERROR; |
85 | | |
86 | 30.7k | UniquePtr<EC_POINT> peer_point(EC_POINT_new(group_)); |
87 | 30.7k | UniquePtr<EC_POINT> result(EC_POINT_new(group_)); |
88 | 30.7k | UniquePtr<BIGNUM> x(BN_new()); |
89 | 30.7k | if (!peer_point || !result || !x) { |
90 | 0 | return false; |
91 | 0 | } |
92 | | |
93 | 30.7k | if (ciphertext.empty() || ciphertext[0] != POINT_CONVERSION_UNCOMPRESSED || |
94 | 30.7k | !EC_POINT_oct2point(group_, peer_point.get(), ciphertext.data(), |
95 | 30.6k | ciphertext.size(), /*ctx=*/nullptr)) { |
96 | 321 | OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); |
97 | 321 | *out_alert = SSL_AD_ILLEGAL_PARAMETER; |
98 | 321 | return false; |
99 | 321 | } |
100 | | |
101 | | // Compute the x-coordinate of |peer_key| * |private_key_|. |
102 | 30.4k | if (!EC_POINT_mul(group_, result.get(), nullptr, peer_point.get(), |
103 | 30.4k | private_key_.get(), /*ctx=*/nullptr) || |
104 | 30.4k | !EC_POINT_get_affine_coordinates_GFp(group_, result.get(), x.get(), |
105 | 30.4k | nullptr, /*ctx=*/nullptr)) { |
106 | 4 | return false; |
107 | 4 | } |
108 | | |
109 | | // Encode the x-coordinate left-padded with zeros. |
110 | 30.4k | Array<uint8_t> secret; |
111 | 30.4k | if (!secret.InitForOverwrite((EC_GROUP_get_degree(group_) + 7) / 8) || |
112 | 30.4k | !BN_bn2bin_padded(secret.data(), secret.size(), x.get())) { |
113 | 0 | return false; |
114 | 0 | } |
115 | | |
116 | 30.4k | *out_secret = std::move(secret); |
117 | 30.4k | return true; |
118 | 30.4k | } |
119 | | |
120 | 0 | bool SerializePrivateKey(CBB *out) override { |
121 | 0 | assert(group_); |
122 | 0 | assert(private_key_); |
123 | | // Padding is added to avoid leaking the length. |
124 | 0 | size_t len = BN_num_bytes(EC_GROUP_get0_order(group_)); |
125 | 0 | return BN_bn2cbb_padded(out, len, private_key_.get()); |
126 | 0 | } |
127 | | |
128 | 564 | bool DeserializePrivateKey(CBS *in) override { |
129 | 564 | assert(!private_key_); |
130 | 564 | private_key_.reset(BN_bin2bn(CBS_data(in), CBS_len(in), nullptr)); |
131 | 564 | return private_key_ != nullptr; |
132 | 564 | } |
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: |
142 | 88.2k | X25519KeyShare() {} |
143 | | |
144 | 2.38k | uint16_t GroupID() const override { return SSL_GROUP_X25519; } |
145 | | |
146 | 88.1k | bool Generate(CBB *out) override { |
147 | 88.1k | uint8_t public_key[32]; |
148 | 88.1k | X25519_keypair(public_key, private_key_); |
149 | 88.1k | return !!CBB_add_bytes(out, public_key, sizeof(public_key)); |
150 | 88.1k | } |
151 | | |
152 | | bool Encap(CBB *out_ciphertext, Array<uint8_t> *out_secret, |
153 | 21.1k | 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 | 21.1k | *out_alert = SSL_AD_INTERNAL_ERROR; |
157 | 21.1k | return Generate(out_ciphertext) && Decap(out_secret, out_alert, peer_key); |
158 | 21.1k | } |
159 | | |
160 | | bool Decap(Array<uint8_t> *out_secret, uint8_t *out_alert, |
161 | 26.0k | Span<const uint8_t> ciphertext) override { |
162 | 26.0k | *out_alert = SSL_AD_INTERNAL_ERROR; |
163 | | |
164 | 26.0k | Array<uint8_t> secret; |
165 | 26.0k | if (!secret.InitForOverwrite(32)) { |
166 | 0 | return false; |
167 | 0 | } |
168 | | |
169 | 26.0k | if (ciphertext.size() != 32 || // |
170 | 26.0k | !X25519(secret.data(), private_key_, ciphertext.data())) { |
171 | 33 | *out_alert = SSL_AD_ILLEGAL_PARAMETER; |
172 | 33 | OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); |
173 | 33 | return false; |
174 | 33 | } |
175 | | |
176 | 26.0k | *out_secret = std::move(secret); |
177 | 26.0k | return true; |
178 | 26.0k | } |
179 | | |
180 | 0 | bool SerializePrivateKey(CBB *out) override { |
181 | 0 | return CBB_add_bytes(out, private_key_, sizeof(private_key_)); |
182 | 0 | } |
183 | | |
184 | 78 | bool DeserializePrivateKey(CBS *in) override { |
185 | 78 | if (CBS_len(in) != sizeof(private_key_) || |
186 | 78 | !CBS_copy_bytes(in, private_key_, sizeof(private_key_))) { |
187 | 17 | return false; |
188 | 17 | } |
189 | 61 | return true; |
190 | 78 | } |
191 | | |
192 | | private: |
193 | | uint8_t private_key_[32]; |
194 | | }; |
195 | | |
196 | | // draft-tls-westerbaan-xyber768d00-03 |
197 | | class X25519Kyber768KeyShare : public SSLKeyShare { |
198 | | public: |
199 | 215 | X25519Kyber768KeyShare() {} |
200 | | |
201 | 1 | uint16_t GroupID() const override { |
202 | 1 | return SSL_GROUP_X25519_KYBER768_DRAFT00; |
203 | 1 | } |
204 | | |
205 | 66 | bool Generate(CBB *out) override { |
206 | 66 | uint8_t x25519_public_key[32]; |
207 | 66 | X25519_keypair(x25519_public_key, x25519_private_key_); |
208 | | |
209 | 66 | uint8_t kyber_public_key[KYBER_PUBLIC_KEY_BYTES]; |
210 | 66 | KYBER_generate_key(kyber_public_key, &kyber_private_key_); |
211 | | |
212 | 66 | if (!CBB_add_bytes(out, x25519_public_key, sizeof(x25519_public_key)) || |
213 | 66 | !CBB_add_bytes(out, kyber_public_key, sizeof(kyber_public_key))) { |
214 | 0 | return false; |
215 | 0 | } |
216 | | |
217 | 66 | return true; |
218 | 66 | } |
219 | | |
220 | | bool Encap(CBB *out_ciphertext, Array<uint8_t> *out_secret, |
221 | 149 | uint8_t *out_alert, Span<const uint8_t> peer_key) override { |
222 | 149 | Array<uint8_t> secret; |
223 | 149 | if (!secret.InitForOverwrite(32 + KYBER_SHARED_SECRET_BYTES)) { |
224 | 0 | return false; |
225 | 0 | } |
226 | | |
227 | 149 | uint8_t x25519_public_key[32]; |
228 | 149 | X25519_keypair(x25519_public_key, x25519_private_key_); |
229 | 149 | KYBER_public_key peer_kyber_pub; |
230 | 149 | CBS peer_key_cbs, peer_x25519_cbs, peer_kyber_cbs; |
231 | 149 | CBS_init(&peer_key_cbs, peer_key.data(), peer_key.size()); |
232 | 149 | if (!CBS_get_bytes(&peer_key_cbs, &peer_x25519_cbs, 32) || |
233 | 149 | !CBS_get_bytes(&peer_key_cbs, &peer_kyber_cbs, |
234 | 147 | KYBER_PUBLIC_KEY_BYTES) || |
235 | 149 | CBS_len(&peer_key_cbs) != 0 || |
236 | 149 | !X25519(secret.data(), x25519_private_key_, |
237 | 141 | CBS_data(&peer_x25519_cbs)) || |
238 | 149 | !KYBER_parse_public_key(&peer_kyber_pub, &peer_kyber_cbs)) { |
239 | 54 | *out_alert = SSL_AD_ILLEGAL_PARAMETER; |
240 | 54 | OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); |
241 | 54 | return false; |
242 | 54 | } |
243 | | |
244 | 95 | uint8_t kyber_ciphertext[KYBER_CIPHERTEXT_BYTES]; |
245 | 95 | KYBER_encap(kyber_ciphertext, secret.data() + 32, &peer_kyber_pub); |
246 | | |
247 | 95 | if (!CBB_add_bytes(out_ciphertext, x25519_public_key, |
248 | 95 | sizeof(x25519_public_key)) || |
249 | 95 | !CBB_add_bytes(out_ciphertext, kyber_ciphertext, |
250 | 95 | sizeof(kyber_ciphertext))) { |
251 | 0 | return false; |
252 | 0 | } |
253 | | |
254 | 95 | *out_secret = std::move(secret); |
255 | 95 | return true; |
256 | 95 | } |
257 | | |
258 | | bool Decap(Array<uint8_t> *out_secret, uint8_t *out_alert, |
259 | 0 | Span<const uint8_t> ciphertext) override { |
260 | 0 | *out_alert = SSL_AD_INTERNAL_ERROR; |
261 | |
|
262 | 0 | Array<uint8_t> secret; |
263 | 0 | if (!secret.InitForOverwrite(32 + KYBER_SHARED_SECRET_BYTES)) { |
264 | 0 | return false; |
265 | 0 | } |
266 | | |
267 | 0 | if (ciphertext.size() != 32 + KYBER_CIPHERTEXT_BYTES || |
268 | 0 | !X25519(secret.data(), x25519_private_key_, ciphertext.data())) { |
269 | 0 | *out_alert = SSL_AD_ILLEGAL_PARAMETER; |
270 | 0 | OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); |
271 | 0 | return false; |
272 | 0 | } |
273 | | |
274 | 0 | KYBER_decap(secret.data() + 32, ciphertext.data() + 32, |
275 | 0 | &kyber_private_key_); |
276 | 0 | *out_secret = std::move(secret); |
277 | 0 | return true; |
278 | 0 | } |
279 | | |
280 | | private: |
281 | | uint8_t x25519_private_key_[32]; |
282 | | KYBER_private_key kyber_private_key_; |
283 | | }; |
284 | | |
285 | | // draft-ietf-tls-ecdhe-mlkem-00 |
286 | | class X25519MLKEM768KeyShare : public SSLKeyShare { |
287 | | public: |
288 | 63.3k | X25519MLKEM768KeyShare() {} |
289 | | |
290 | 2.41k | uint16_t GroupID() const override { return SSL_GROUP_X25519_MLKEM768; } |
291 | | |
292 | 63.0k | bool Generate(CBB *out) override { |
293 | 63.0k | uint8_t mlkem_public_key[MLKEM768_PUBLIC_KEY_BYTES]; |
294 | 63.0k | MLKEM768_generate_key(mlkem_public_key, /*optional_out_seed=*/nullptr, |
295 | 63.0k | &mlkem_private_key_); |
296 | | |
297 | 63.0k | uint8_t x25519_public_key[X25519_PUBLIC_VALUE_LEN]; |
298 | 63.0k | X25519_keypair(x25519_public_key, x25519_private_key_); |
299 | | |
300 | 63.0k | if (!CBB_add_bytes(out, mlkem_public_key, sizeof(mlkem_public_key)) || |
301 | 63.0k | !CBB_add_bytes(out, x25519_public_key, sizeof(x25519_public_key))) { |
302 | 0 | return false; |
303 | 0 | } |
304 | | |
305 | 63.0k | return true; |
306 | 63.0k | } |
307 | | |
308 | | bool Encap(CBB *out_ciphertext, Array<uint8_t> *out_secret, |
309 | 295 | uint8_t *out_alert, Span<const uint8_t> peer_key) override { |
310 | 295 | Array<uint8_t> secret; |
311 | 295 | if (!secret.InitForOverwrite(MLKEM_SHARED_SECRET_BYTES + |
312 | 295 | X25519_SHARED_KEY_LEN)) { |
313 | 0 | return false; |
314 | 0 | } |
315 | | |
316 | 295 | MLKEM768_public_key peer_mlkem_pub; |
317 | 295 | uint8_t x25519_public_key[X25519_PUBLIC_VALUE_LEN]; |
318 | 295 | X25519_keypair(x25519_public_key, x25519_private_key_); |
319 | 295 | CBS peer_key_cbs, peer_mlkem_cbs, peer_x25519_cbs; |
320 | 295 | CBS_init(&peer_key_cbs, peer_key.data(), peer_key.size()); |
321 | 295 | if (!CBS_get_bytes(&peer_key_cbs, &peer_mlkem_cbs, |
322 | 295 | MLKEM768_PUBLIC_KEY_BYTES) || |
323 | 295 | !MLKEM768_parse_public_key(&peer_mlkem_pub, &peer_mlkem_cbs) || |
324 | 295 | !CBS_get_bytes(&peer_key_cbs, &peer_x25519_cbs, |
325 | 245 | X25519_PUBLIC_VALUE_LEN) || |
326 | 295 | CBS_len(&peer_key_cbs) != 0 || |
327 | 295 | !X25519(secret.data() + MLKEM_SHARED_SECRET_BYTES, x25519_private_key_, |
328 | 212 | CBS_data(&peer_x25519_cbs))) { |
329 | 87 | *out_alert = SSL_AD_ILLEGAL_PARAMETER; |
330 | 87 | OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); |
331 | 87 | return false; |
332 | 87 | } |
333 | | |
334 | 208 | uint8_t mlkem_ciphertext[MLKEM768_CIPHERTEXT_BYTES]; |
335 | 208 | MLKEM768_encap(mlkem_ciphertext, secret.data(), &peer_mlkem_pub); |
336 | | |
337 | 208 | if (!CBB_add_bytes(out_ciphertext, mlkem_ciphertext, |
338 | 208 | sizeof(mlkem_ciphertext)) || |
339 | 208 | !CBB_add_bytes(out_ciphertext, x25519_public_key, |
340 | 208 | sizeof(x25519_public_key))) { |
341 | 0 | return false; |
342 | 0 | } |
343 | | |
344 | 208 | *out_secret = std::move(secret); |
345 | 208 | return true; |
346 | 208 | } |
347 | | |
348 | | bool Decap(Array<uint8_t> *out_secret, uint8_t *out_alert, |
349 | 22 | Span<const uint8_t> ciphertext) override { |
350 | 22 | *out_alert = SSL_AD_INTERNAL_ERROR; |
351 | | |
352 | 22 | Array<uint8_t> secret; |
353 | 22 | if (!secret.InitForOverwrite(MLKEM_SHARED_SECRET_BYTES + |
354 | 22 | X25519_SHARED_KEY_LEN)) { |
355 | 0 | return false; |
356 | 0 | } |
357 | | |
358 | 22 | if (ciphertext.size() != |
359 | 22 | MLKEM768_CIPHERTEXT_BYTES + X25519_PUBLIC_VALUE_LEN || |
360 | 22 | !MLKEM768_decap(secret.data(), ciphertext.data(), |
361 | 21 | MLKEM768_CIPHERTEXT_BYTES, &mlkem_private_key_) || |
362 | 22 | !X25519(secret.data() + MLKEM_SHARED_SECRET_BYTES, x25519_private_key_, |
363 | 21 | ciphertext.data() + MLKEM768_CIPHERTEXT_BYTES)) { |
364 | 7 | *out_alert = SSL_AD_ILLEGAL_PARAMETER; |
365 | 7 | OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); |
366 | 7 | return false; |
367 | 7 | } |
368 | | |
369 | 15 | *out_secret = std::move(secret); |
370 | 15 | return true; |
371 | 22 | } |
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_X9_62_prime256v1, SSL_GROUP_SECP256R1, "P-256", "prime256v1"}, |
380 | | {NID_secp384r1, SSL_GROUP_SECP384R1, "P-384", "secp384r1"}, |
381 | | {NID_secp521r1, SSL_GROUP_SECP521R1, "P-521", "secp521r1"}, |
382 | | {NID_X25519, SSL_GROUP_X25519, "X25519", "x25519"}, |
383 | | {NID_X25519Kyber768Draft00, SSL_GROUP_X25519_KYBER768_DRAFT00, |
384 | | "X25519Kyber768Draft00", ""}, |
385 | | {NID_X25519MLKEM768, SSL_GROUP_X25519_MLKEM768, "X25519MLKEM768", ""}, |
386 | | }; |
387 | | |
388 | | } // namespace |
389 | | |
390 | 0 | Span<const NamedGroup> NamedGroups() { return kNamedGroups; } |
391 | | |
392 | 183k | UniquePtr<SSLKeyShare> SSLKeyShare::Create(uint16_t group_id) { |
393 | 183k | switch (group_id) { |
394 | 3.67k | case SSL_GROUP_SECP256R1: |
395 | 3.67k | return MakeUnique<ECKeyShare>(EC_group_p256(), SSL_GROUP_SECP256R1); |
396 | 28.0k | case SSL_GROUP_SECP384R1: |
397 | 28.0k | return MakeUnique<ECKeyShare>(EC_group_p384(), SSL_GROUP_SECP384R1); |
398 | 386 | case SSL_GROUP_SECP521R1: |
399 | 386 | return MakeUnique<ECKeyShare>(EC_group_p521(), SSL_GROUP_SECP521R1); |
400 | 88.2k | case SSL_GROUP_X25519: |
401 | 88.2k | return MakeUnique<X25519KeyShare>(); |
402 | 215 | case SSL_GROUP_X25519_KYBER768_DRAFT00: |
403 | 215 | return MakeUnique<X25519Kyber768KeyShare>(); |
404 | 63.3k | case SSL_GROUP_X25519_MLKEM768: |
405 | 63.3k | return MakeUnique<X25519MLKEM768KeyShare>(); |
406 | 0 | default: |
407 | 0 | return nullptr; |
408 | 183k | } |
409 | 183k | } |
410 | | |
411 | 20.2k | bool ssl_nid_to_group_id(uint16_t *out_group_id, int nid) { |
412 | 45.3k | for (const auto &group : kNamedGroups) { |
413 | 45.3k | if (group.nid == nid) { |
414 | 15.4k | *out_group_id = group.group_id; |
415 | 15.4k | return true; |
416 | 15.4k | } |
417 | 45.3k | } |
418 | 4.81k | return false; |
419 | 20.2k | } |
420 | | |
421 | | bool ssl_name_to_group_id(uint16_t *out_group_id, const char *name, |
422 | 6.55k | size_t len) { |
423 | 34.4k | for (const auto &group : kNamedGroups) { |
424 | 34.4k | if (len == strlen(group.name) && // |
425 | 34.4k | !strncmp(group.name, name, len)) { |
426 | 1.06k | *out_group_id = group.group_id; |
427 | 1.06k | return true; |
428 | 1.06k | } |
429 | 33.3k | if (strlen(group.alias) > 0 && len == strlen(group.alias) && |
430 | 33.3k | !strncmp(group.alias, name, len)) { |
431 | 296 | *out_group_id = group.group_id; |
432 | 296 | return true; |
433 | 296 | } |
434 | 33.3k | } |
435 | 5.20k | return false; |
436 | 6.55k | } |
437 | | |
438 | 3.04k | int ssl_group_id_to_nid(uint16_t group_id) { |
439 | 14.6k | for (const auto &group : kNamedGroups) { |
440 | 14.6k | if (group.group_id == group_id) { |
441 | 942 | return group.nid; |
442 | 942 | } |
443 | 14.6k | } |
444 | 2.10k | return NID_undef; |
445 | 3.04k | } |
446 | | |
447 | | BSSL_NAMESPACE_END |
448 | | |
449 | | using namespace bssl; |
450 | | |
451 | 0 | const char *SSL_get_group_name(uint16_t group_id) { |
452 | 0 | for (const auto &group : kNamedGroups) { |
453 | 0 | if (group.group_id == group_id) { |
454 | 0 | return group.name; |
455 | 0 | } |
456 | 0 | } |
457 | 0 | return nullptr; |
458 | 0 | } |
459 | | |
460 | 0 | size_t SSL_get_all_group_names(const char **out, size_t max_out) { |
461 | 0 | return GetAllNames(out, max_out, Span<const char *>(), &NamedGroup::name, |
462 | 0 | Span(kNamedGroups)); |
463 | 0 | } |