1 // Copyright 2021 Google LLC
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 // http://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 ///////////////////////////////////////////////////////////////////////////////
16 #include "tink/internal/ec_util.h"
17
18 #include <algorithm>
19 #include <cstdint>
20 #include <cstring>
21 #include <memory>
22 #include <string>
23 #include <utility>
24 #include <vector>
25
26 #include "absl/memory/memory.h"
27 #include "absl/status/status.h"
28 #include "absl/strings/str_cat.h"
29 #include "absl/strings/string_view.h"
30 #include "absl/types/span.h"
31 #include "openssl/bn.h"
32 #include "openssl/ec.h"
33 #include "openssl/crypto.h"
34 #include "openssl/ecdsa.h"
35 #include "openssl/evp.h"
36 #include "tink/internal/bn_util.h"
37 #include "tink/internal/err_util.h"
38 #include "tink/internal/fips_utils.h"
39 #include "tink/internal/ssl_unique_ptr.h"
40 #include "tink/subtle/common_enums.h"
41 #include "tink/subtle/random.h"
42 #include "tink/subtle/subtle_util.h"
43 #include "tink/util/secret_data.h"
44 #include "tink/util/status.h"
45 #include "tink/util/statusor.h"
46
47 namespace crypto {
48 namespace tink {
49 namespace internal {
50 namespace {
51
52 using ::crypto::tink::subtle::EcPointFormat;
53 using ::crypto::tink::subtle::EllipticCurveType;
54
55 // Encodes the given `point` to string, according to a `conversion_form`.
SslEcPointEncode(EC_GROUP * group,const EC_POINT * point,point_conversion_form_t conversion_form)56 util::StatusOr<std::string> SslEcPointEncode(
57 EC_GROUP *group, const EC_POINT *point,
58 point_conversion_form_t conversion_form) {
59 // Get the buffer size first passing a NULL buffer.
60 size_t buffer_size =
61 EC_POINT_point2oct(group, point, conversion_form,
62 /*buf=*/nullptr, /*len=*/0, /*ctx=*/nullptr);
63 if (buffer_size == 0) {
64 return util::Status(absl::StatusCode::kInternal,
65 "EC_POINT_point2oct failed");
66 }
67
68 std::string encoded_point;
69 subtle::ResizeStringUninitialized(&encoded_point, buffer_size);
70 size_t size =
71 EC_POINT_point2oct(group, point, conversion_form,
72 reinterpret_cast<uint8_t *>(&encoded_point[0]),
73 buffer_size, /*ctx=*/nullptr);
74 if (size == 0) {
75 return util::Status(absl::StatusCode::kInternal,
76 "EC_POINT_point2oct failed");
77 }
78 return encoded_point;
79 }
80
81 // Returns an EC_POINT from `group`, and encoded (bigendian string
82 // representation of BIGNUMs) point coordinates `pubx`, `puby`.
SslGetEcPointFromCoordinates(const EC_GROUP * group,absl::string_view pubx,absl::string_view puby)83 util::StatusOr<SslUniquePtr<EC_POINT>> SslGetEcPointFromCoordinates(
84 const EC_GROUP *group, absl::string_view pubx, absl::string_view puby) {
85 util::StatusOr<SslUniquePtr<BIGNUM>> bn_x = StringToBignum(pubx);
86 if (!bn_x.ok()) {
87 return bn_x.status();
88 }
89 util::StatusOr<SslUniquePtr<BIGNUM>> bn_y = StringToBignum(puby);
90 if (!bn_y.ok()) {
91 return bn_y.status();
92 }
93 SslUniquePtr<EC_POINT> pub_key(EC_POINT_new(group));
94 // In BoringSSL and OpenSSL > 1.1.0 EC_POINT_set_affine_coordinates_GFp
95 // already checkes if the point is on the curve.
96 if (EC_POINT_set_affine_coordinates_GFp(group, pub_key.get(), bn_x->get(),
97 bn_y->get(), nullptr) != 1) {
98 return util::Status(absl::StatusCode::kInternal,
99 "EC_POINT_set_affine_coordinates_GFp failed");
100 }
101 return std::move(pub_key);
102 }
103
104 // Returns an EC_POINT from an `encoded` point with format `format` and curve
105 // type `curve`. `format` is either COMPRESSED or UNCOMPRESSED.
SslGetEcPointFromEncoded(EllipticCurveType curve,EcPointFormat format,absl::string_view encoded)106 util::StatusOr<SslUniquePtr<EC_POINT>> SslGetEcPointFromEncoded(
107 EllipticCurveType curve, EcPointFormat format, absl::string_view encoded) {
108 if (format != EcPointFormat::UNCOMPRESSED &&
109 format != EcPointFormat::COMPRESSED) {
110 return util::Status(
111 absl::StatusCode::kInvalidArgument,
112 absl::StrCat("Invalid format ", subtle::EnumToString(format)));
113 }
114 util::StatusOr<SslUniquePtr<EC_GROUP>> group = EcGroupFromCurveType(curve);
115 if (!group.ok()) {
116 return group.status();
117 }
118
119 util::StatusOr<int32_t> encoding_size =
120 EcPointEncodingSizeInBytes(curve, format);
121 if (!encoding_size.ok()) {
122 return encoding_size.status();
123 }
124 if (encoded.size() != *encoding_size) {
125 return util::Status(absl::StatusCode::kInternal,
126 absl::StrCat("Encoded point's size is ", encoded.size(),
127 " bytes; expected ", *encoding_size));
128 }
129
130 // Check starting byte.
131 if (format == EcPointFormat::UNCOMPRESSED &&
132 static_cast<int>(encoded[0]) != 0x04) {
133 return util::Status(
134 absl::StatusCode::kInternal,
135 "Uncompressed point should start with 0x04, but input doesn't");
136 } else if (format == EcPointFormat::COMPRESSED &&
137 static_cast<int>(encoded[0]) != 0x03 &&
138 static_cast<int>(encoded[0]) != 0x02) {
139 return util::Status(absl::StatusCode::kInternal,
140 "Compressed point should start with either 0x02 or "
141 "0x03, but input doesn't");
142 }
143
144 SslUniquePtr<EC_POINT> point(EC_POINT_new(group->get()));
145 if (EC_POINT_oct2point(group->get(), point.get(),
146 reinterpret_cast<const uint8_t *>(encoded.data()),
147 encoded.size(), nullptr) != 1) {
148 return util::Status(absl::StatusCode::kInternal,
149 "EC_POINT_toc2point failed");
150 }
151 // Check that point is on curve.
152 if (EC_POINT_is_on_curve(group->get(), point.get(), nullptr) != 1) {
153 return util::Status(absl::StatusCode::kInternal, "Point is not on curve");
154 }
155
156 return std::move(point);
157 }
158
159 // OpenSSL/BoringSSL's EC_POINT as a pair of BIGNUMs.
160 struct EcPointCoordinates {
161 SslUniquePtr<BIGNUM> x;
162 SslUniquePtr<BIGNUM> y;
163 };
164
165 // Returns a given `point` as a pair of BIGNUMs. Precondition: `group` and
166 // `point` are not null.
SslGetEcPointCoordinates(const EC_GROUP * group,const EC_POINT * point)167 util::StatusOr<EcPointCoordinates> SslGetEcPointCoordinates(
168 const EC_GROUP *group, const EC_POINT *point) {
169 EcPointCoordinates coordinates = {
170 SslUniquePtr<BIGNUM>(BN_new()),
171 SslUniquePtr<BIGNUM>(BN_new()),
172 };
173 if (coordinates.x == nullptr || coordinates.y == nullptr) {
174 return util::Status(absl::StatusCode::kInternal,
175 "Unable to allocate memory for the point coordinates");
176 }
177 if (EC_POINT_get_affine_coordinates_GFp(group, point, coordinates.x.get(),
178 coordinates.y.get(), nullptr) != 1) {
179 return util::Status(absl::StatusCode::kInternal,
180 "EC_POINT_get_affine_coordinates_GFp failed");
181 }
182 return std::move(coordinates);
183 }
184
ScalarSizeInBytes(const EC_GROUP * group)185 size_t ScalarSizeInBytes(const EC_GROUP *group) {
186 return BN_num_bytes(EC_GROUP_get0_order(group));
187 }
188
SslEcFieldSizeInBytes(const EC_GROUP * group)189 size_t SslEcFieldSizeInBytes(const EC_GROUP *group) {
190 unsigned degree_bits = EC_GROUP_get_degree(group);
191 return (degree_bits + 7) / 8;
192 }
193
194 // Given an OpenSSL/BoringSSL key EC_KEY `key` and curve type `curve` return an
195 // EcKey.
EcKeyFromSslEcKey(EllipticCurveType curve,const EC_KEY & key)196 util::StatusOr<EcKey> EcKeyFromSslEcKey(EllipticCurveType curve,
197 const EC_KEY &key) {
198 util::StatusOr<SslUniquePtr<EC_GROUP>> group = EcGroupFromCurveType(curve);
199 if (!group.ok()) {
200 return group.status();
201 }
202 const BIGNUM *priv_key = EC_KEY_get0_private_key(&key);
203 const EC_POINT *pub_key = EC_KEY_get0_public_key(&key);
204
205 util::StatusOr<EcPointCoordinates> pub_key_bns =
206 SslGetEcPointCoordinates(group->get(), pub_key);
207 if (!pub_key_bns.ok()) {
208 return pub_key_bns.status();
209 }
210
211 const int kFieldElementSizeInBytes = SslEcFieldSizeInBytes(group->get());
212
213 util::StatusOr<std::string> pub_x_str =
214 BignumToString(pub_key_bns->x.get(), kFieldElementSizeInBytes);
215 if (!pub_x_str.ok()) {
216 return pub_x_str.status();
217 }
218 util::StatusOr<std::string> pub_y_str =
219 BignumToString(pub_key_bns->y.get(), kFieldElementSizeInBytes);
220 if (!pub_y_str.ok()) {
221 return pub_y_str.status();
222 }
223 util::StatusOr<util::SecretData> priv_key_data =
224 BignumToSecretData(priv_key, ScalarSizeInBytes(group->get()));
225 if (!priv_key_data.ok()) {
226 return priv_key_data.status();
227 }
228 EcKey ec_key = {
229 /*curve=*/curve,
230 /*pub_x=*/*std::move(pub_x_str),
231 /*pub_y=*/*std::move(pub_y_str),
232 /*priv=*/*std::move(priv_key_data),
233 };
234 return ec_key;
235 }
236
237 enum SslEvpPkeyType {
238 kX25519Key = EVP_PKEY_X25519,
239 kEd25519Key = EVP_PKEY_ED25519
240 };
241
242 // Returns a new EVP_PKEY key from the given `key_type`.
SslNewEvpKey(SslEvpPkeyType key_type)243 util::StatusOr<SslUniquePtr<EVP_PKEY>> SslNewEvpKey(SslEvpPkeyType key_type) {
244 EVP_PKEY *private_key = nullptr;
245 SslUniquePtr<EVP_PKEY_CTX> pctx(EVP_PKEY_CTX_new_id(key_type, /*e=*/nullptr));
246 if (pctx == nullptr) {
247 return util::Status(
248 absl::StatusCode::kInternal,
249 absl::StrCat("EVP_PKEY_CTX_new_id failed for id ", key_type));
250 }
251
252 if (EVP_PKEY_keygen_init(pctx.get()) != 1) {
253 return util::Status(absl::StatusCode::kInternal,
254 "EVP_PKEY_keygen_init failed");
255 }
256 if (EVP_PKEY_keygen(pctx.get(), &private_key) != 1) {
257 return util::Status(absl::StatusCode::kInternal, "EVP_PKEY_keygen failed");
258 }
259 return {SslUniquePtr<EVP_PKEY>(private_key)};
260 }
261
262 // Given a private EVP_PKEY `evp_key` of key type `key_type` fills `priv_key`
263 // and `pub_key` with raw private and public keys, respectively.
SslNewKeyPairFromEcKey(SslEvpPkeyType key_type,const EVP_PKEY & evp_key,absl::Span<uint8_t> priv_key,absl::Span<uint8_t> pub_key)264 util::Status SslNewKeyPairFromEcKey(SslEvpPkeyType key_type,
265 const EVP_PKEY &evp_key,
266 absl::Span<uint8_t> priv_key,
267 absl::Span<uint8_t> pub_key) {
268 size_t len = priv_key.size();
269 if (EVP_PKEY_get_raw_private_key(&evp_key, priv_key.data(), &len) != 1) {
270 return util::Status(absl::StatusCode::kInternal,
271 "EVP_PKEY_get_raw_private_key failed");
272 }
273 if (len != priv_key.size()) {
274 return util::Status(absl::StatusCode::kInternal,
275 absl::StrCat("Invalid private key size; expected ",
276 priv_key.size(), " got ", len));
277 }
278
279 len = pub_key.size();
280 if (EVP_PKEY_get_raw_public_key(&evp_key, pub_key.data(), &len) != 1) {
281 return util::Status(absl::StatusCode::kInternal,
282 "EVP_PKEY_get_raw_public_key failed");
283 }
284 if (len != pub_key.size()) {
285 return util::Status(absl::StatusCode::kInternal,
286 absl::StrCat("Invalid public key size; expected ",
287 pub_key.size(), " got ", len));
288 }
289
290 return util::OkStatus();
291 }
292
SslEcdsaSignatureToBytes(const ECDSA_SIG * ecdsa_signature)293 util::StatusOr<std::string> SslEcdsaSignatureToBytes(
294 const ECDSA_SIG *ecdsa_signature) {
295 if (ecdsa_signature == nullptr) {
296 return util::Status(absl::StatusCode::kInvalidArgument,
297 "ECDSA signature is null");
298 }
299 uint8_t *der = nullptr;
300 int der_len = i2d_ECDSA_SIG(ecdsa_signature, &der);
301 if (der_len <= 0) {
302 return util::Status(absl::StatusCode::kInternal, "i2d_ECDSA_SIG failed");
303 }
304 auto result = std::string(reinterpret_cast<char *>(der), der_len);
305 OPENSSL_free(der);
306 return result;
307 }
308
309 } // namespace
310
EcFieldSizeInBytes(EllipticCurveType curve_type)311 util::StatusOr<int32_t> EcFieldSizeInBytes(EllipticCurveType curve_type) {
312 if (curve_type == EllipticCurveType::CURVE25519) {
313 return 32;
314 }
315 util::StatusOr<SslUniquePtr<EC_GROUP>> ec_group =
316 EcGroupFromCurveType(curve_type);
317 if (!ec_group.ok()) {
318 return ec_group.status();
319 }
320 return SslEcFieldSizeInBytes(ec_group->get());
321 }
322
EcPointEncodingSizeInBytes(EllipticCurveType curve_type,EcPointFormat point_format)323 util::StatusOr<int32_t> EcPointEncodingSizeInBytes(EllipticCurveType curve_type,
324 EcPointFormat point_format) {
325 util::StatusOr<int32_t> coordinate_size = EcFieldSizeInBytes(curve_type);
326 if (!coordinate_size.ok()) {
327 return coordinate_size.status();
328 }
329 if (curve_type == EllipticCurveType::CURVE25519) {
330 return coordinate_size;
331 }
332 if (*coordinate_size == 0) {
333 return util::Status(absl::StatusCode::kInvalidArgument,
334 absl::StrCat("Unsupported elliptic curve type: ",
335 EnumToString(curve_type)));
336 }
337 switch (point_format) {
338 case EcPointFormat::UNCOMPRESSED:
339 return 2 * (*coordinate_size) + 1;
340 case EcPointFormat::COMPRESSED:
341 return (*coordinate_size) + 1;
342 case EcPointFormat::DO_NOT_USE_CRUNCHY_UNCOMPRESSED:
343 return 2 * (*coordinate_size);
344 default:
345 return util::Status(
346 absl::StatusCode::kInvalidArgument,
347 absl::StrCat("Unsupported elliptic curve point format: ",
348 EnumToString(point_format)));
349 }
350 }
351
NewEcKey(EllipticCurveType curve_type)352 util::StatusOr<EcKey> NewEcKey(EllipticCurveType curve_type) {
353 if (curve_type == EllipticCurveType::CURVE25519) {
354 util::StatusOr<std::unique_ptr<X25519Key>> key = NewX25519Key();
355 if (!key.ok()) {
356 return key.status();
357 }
358 return EcKeyFromX25519Key(key->get());
359 }
360 util::StatusOr<SslUniquePtr<EC_GROUP>> group =
361 EcGroupFromCurveType(curve_type);
362 if (!group.ok()) {
363 return group.status();
364 }
365 SslUniquePtr<EC_KEY> key(EC_KEY_new());
366
367 if (key.get() == nullptr) {
368 return util::Status(absl::StatusCode::kInternal, "EC_KEY_new failed");
369 }
370 EC_KEY_set_group(key.get(), group->get());
371 EC_KEY_generate_key(key.get());
372 return EcKeyFromSslEcKey(curve_type, *key);
373 }
374
NewEcKey(EllipticCurveType curve_type,const util::SecretData & secret_seed)375 util::StatusOr<EcKey> NewEcKey(EllipticCurveType curve_type,
376 const util::SecretData &secret_seed) {
377 // EC_KEY_derive_from_secret() is neither defined in the version of BoringSSL
378 // used when FIPS-only mode is enabled at compile time, nor currently
379 // implemented for OpenSSL.
380 #if defined(TINK_USE_ONLY_FIPS)
381 return util::Status(
382 absl::StatusCode::kUnimplemented,
383 "Deriving EC keys from a secret seed is not allowed in FIPS mode");
384 #elif !defined(OPENSSL_IS_BORINGSSL)
385 return util::Status(
386 absl::StatusCode::kUnimplemented,
387 "Deriving EC keys from a secret seed is not supported with OpenSSL");
388 #else
389 if (IsFipsModeEnabled()) {
390 return util::Status(
391 absl::StatusCode::kInternal,
392 "Deriving EC keys from a secret seed is not allowed in FIPS mode");
393 }
394 if (curve_type == EllipticCurveType::CURVE25519) {
395 return util::Status(
396 absl::StatusCode::kInternal,
397 "Creating a X25519 key from a secret seed is not supported");
398 }
399 util::StatusOr<SslUniquePtr<EC_GROUP>> group =
400 EcGroupFromCurveType(curve_type);
401 if (!group.ok()) {
402 return group.status();
403 }
404 SslUniquePtr<EC_KEY> key(EC_KEY_derive_from_secret(
405 group->get(), secret_seed.data(), secret_seed.size()));
406 if (key.get() == nullptr) {
407 return util::Status(absl::StatusCode::kInternal,
408 "EC_KEY_derive_from_secret failed");
409 }
410 return EcKeyFromSslEcKey(curve_type, *key);
411 #endif
412 }
413
NewX25519Key()414 util::StatusOr<std::unique_ptr<X25519Key>> NewX25519Key() {
415 util::StatusOr<SslUniquePtr<EVP_PKEY>> private_key =
416 SslNewEvpKey(SslEvpPkeyType::kX25519Key);
417 if (!private_key.ok()) {
418 return private_key.status();
419 }
420
421 auto key = absl::make_unique<X25519Key>();
422 util::Status res = SslNewKeyPairFromEcKey(
423 SslEvpPkeyType::kX25519Key, **private_key,
424 absl::MakeSpan(key->private_key, X25519KeyPrivKeySize()),
425 absl::MakeSpan(key->public_value, X25519KeyPubKeySize()));
426 if (!res.ok()) {
427 return res;
428 }
429 return std::move(key);
430 }
431
EcKeyFromX25519Key(const X25519Key * x25519_key)432 EcKey EcKeyFromX25519Key(const X25519Key *x25519_key) {
433 EcKey ec_key;
434 ec_key.curve = subtle::EllipticCurveType::CURVE25519;
435 // Curve25519 public key is x, not (x,y).
436 ec_key.pub_x =
437 std::string(reinterpret_cast<const char *>(x25519_key->public_value),
438 X25519KeyPubKeySize());
439 ec_key.priv = util::SecretData(std::begin(x25519_key->private_key),
440 std::end(x25519_key->private_key));
441 return ec_key;
442 }
443
NewEd25519Key()444 util::StatusOr<std::unique_ptr<Ed25519Key>> NewEd25519Key() {
445 util::SecretData seed =
446 subtle::Random::GetRandomKeyBytes(Ed25519KeyPrivKeySize());
447 return NewEd25519Key(seed);
448 }
449
NewEd25519Key(const util::SecretData & secret_seed)450 util::StatusOr<std::unique_ptr<Ed25519Key>> NewEd25519Key(
451 const util::SecretData &secret_seed) {
452 if (secret_seed.size() != Ed25519KeyPrivKeySize()) {
453 return util::Status(
454 absl::StatusCode::kInvalidArgument,
455 absl::StrCat("Invalid seed of length ", secret_seed.size(),
456 "; expected ", Ed25519KeyPrivKeySize()));
457 }
458
459 // In BoringSSL this calls ED25519_keypair_from_seed. Accessing the public key
460 // with EVP_PKEY_get_raw_public_key returns the last 32 bytes of the private
461 // key stored by BoringSSL.
462 SslUniquePtr<EVP_PKEY> priv_key(EVP_PKEY_new_raw_private_key(
463 SslEvpPkeyType::kEd25519Key, nullptr, secret_seed.data(),
464 Ed25519KeyPrivKeySize()));
465 if (priv_key == nullptr) {
466 return util::Status(absl::StatusCode::kInternal,
467 "EVP_PKEY_new_raw_private_key failed");
468 }
469
470 auto key = absl::make_unique<Ed25519Key>();
471 subtle::ResizeStringUninitialized(&key->private_key, Ed25519KeyPrivKeySize());
472 subtle::ResizeStringUninitialized(&key->public_key, Ed25519KeyPubKeySize());
473 uint8_t *priv_key_ptr = reinterpret_cast<uint8_t *>(&key->private_key[0]);
474 uint8_t *pub_key_ptr = reinterpret_cast<uint8_t *>(&key->public_key[0]);
475 // The EVP_PKEY interface returns only the first 32 bytes of the private key.
476 util::Status res = SslNewKeyPairFromEcKey(
477 SslEvpPkeyType::kEd25519Key, *priv_key,
478 absl::MakeSpan(priv_key_ptr, Ed25519KeyPrivKeySize()),
479 absl::MakeSpan(pub_key_ptr, Ed25519KeyPubKeySize()));
480 if (!res.ok()) {
481 return res;
482 }
483 return std::move(key);
484 }
485
X25519KeyFromEcKey(const EcKey & ec_key)486 util::StatusOr<std::unique_ptr<X25519Key>> X25519KeyFromEcKey(
487 const EcKey &ec_key) {
488 auto x25519_key = absl::make_unique<X25519Key>();
489 if (ec_key.curve != subtle::EllipticCurveType::CURVE25519) {
490 return util::Status(absl::StatusCode::kInvalidArgument,
491 "This key is not on curve 25519");
492 }
493 if (!ec_key.pub_y.empty()) {
494 return util::Status(absl::StatusCode::kInvalidArgument,
495 "Invalid X25519 key. pub_y is unexpectedly set.");
496 }
497 // Curve25519 public key is x, not (x,y).
498 std::copy_n(ec_key.pub_x.begin(), X25519KeyPubKeySize(),
499 std::begin(x25519_key->public_value));
500 std::copy_n(ec_key.priv.begin(), X25519KeyPrivKeySize(),
501 std::begin(x25519_key->private_key));
502 return std::move(x25519_key);
503 }
504
ComputeX25519SharedSecret(EVP_PKEY * private_key,EVP_PKEY * peer_public_key)505 util::StatusOr<util::SecretData> ComputeX25519SharedSecret(
506 EVP_PKEY *private_key, EVP_PKEY *peer_public_key) {
507 // Make sure the keys are actually X25519 keys.
508 if (EVP_PKEY_id(private_key) != SslEvpPkeyType::kX25519Key) {
509 return util::Status(absl::StatusCode::kInvalidArgument,
510 "Invalid type for private key");
511 }
512 if (EVP_PKEY_id(peer_public_key) != SslEvpPkeyType::kX25519Key) {
513 return util::Status(absl::StatusCode::kInvalidArgument,
514 "Invalid type for peer's public key");
515 }
516
517 internal::SslUniquePtr<EVP_PKEY_CTX> pctx(
518 EVP_PKEY_CTX_new(private_key, nullptr));
519 util::SecretData shared_secret(internal::X25519KeySharedKeySize());
520 size_t out_key_length = shared_secret.size();
521 if (EVP_PKEY_derive_init(pctx.get()) <= 0 ||
522 EVP_PKEY_derive_set_peer(pctx.get(), peer_public_key) <= 0 ||
523 EVP_PKEY_derive(pctx.get(), shared_secret.data(), &out_key_length) <= 0) {
524 return util::Status(absl::StatusCode::kInternal,
525 "Secret generation failed");
526 }
527 return shared_secret;
528 }
529
X25519KeyFromPrivateKey(const util::SecretData & private_key)530 util::StatusOr<std::unique_ptr<X25519Key>> X25519KeyFromPrivateKey(
531 const util::SecretData &private_key) {
532 if (private_key.size() != X25519KeyPrivKeySize()) {
533 return util::Status(absl::StatusCode::kInvalidArgument,
534 "Invalid length for private key");
535 }
536
537 internal::SslUniquePtr<EVP_PKEY> pkey(
538 EVP_PKEY_new_raw_private_key(SslEvpPkeyType::kX25519Key, nullptr,
539 private_key.data(), private_key.size()));
540 auto key = absl::make_unique<X25519Key>();
541 util::Status res = SslNewKeyPairFromEcKey(
542 SslEvpPkeyType::kX25519Key, *pkey,
543 absl::MakeSpan(key->private_key, X25519KeyPrivKeySize()),
544 absl::MakeSpan(key->public_value, X25519KeyPubKeySize()));
545 if (!res.ok()) {
546 return res;
547 }
548 return std::move(key);
549 }
550
EcPointEncode(EllipticCurveType curve,EcPointFormat format,const EC_POINT * point)551 util::StatusOr<std::string> EcPointEncode(EllipticCurveType curve,
552 EcPointFormat format,
553 const EC_POINT *point) {
554 util::StatusOr<SslUniquePtr<EC_GROUP>> group = EcGroupFromCurveType(curve);
555 if (!group.ok()) {
556 return group.status();
557 }
558 if (EC_POINT_is_on_curve(group->get(), point, nullptr) != 1) {
559 return util::Status(absl::StatusCode::kInternal, "Point is not on curve");
560 }
561 switch (format) {
562 case EcPointFormat::UNCOMPRESSED: {
563 return SslEcPointEncode(group->get(), point,
564 POINT_CONVERSION_UNCOMPRESSED);
565 }
566 case EcPointFormat::COMPRESSED: {
567 return SslEcPointEncode(group->get(), point, POINT_CONVERSION_COMPRESSED);
568 }
569 case EcPointFormat::DO_NOT_USE_CRUNCHY_UNCOMPRESSED: {
570 util::StatusOr<EcPointCoordinates> ec_point_xy =
571 SslGetEcPointCoordinates(group->get(), point);
572 if (!ec_point_xy.ok()) {
573 return ec_point_xy.status();
574 }
575 const int kCurveSizeInBytes = SslEcFieldSizeInBytes(group->get());
576 std::string encoded_point;
577 subtle::ResizeStringUninitialized(&encoded_point, 2 * kCurveSizeInBytes);
578 util::Status res = BignumToBinaryPadded(
579 absl::MakeSpan(&encoded_point[0], kCurveSizeInBytes),
580 ec_point_xy->x.get());
581 if (!res.ok()) {
582 return util::Status(
583 absl::StatusCode::kInternal,
584 absl::StrCat(res.message(), " serializing the x coordinate"));
585 }
586
587 res = BignumToBinaryPadded(
588 absl::MakeSpan(&encoded_point[kCurveSizeInBytes], kCurveSizeInBytes),
589 ec_point_xy->y.get());
590 if (!res.ok()) {
591 return util::Status(
592 absl::StatusCode::kInternal,
593 absl::StrCat(res.message(), " serializing the y coordinate"));
594 }
595 return encoded_point;
596 }
597 default:
598 return util::Status(absl::StatusCode::kInternal,
599 "Unsupported point format");
600 }
601 }
602
EcPointDecode(EllipticCurveType curve,EcPointFormat format,absl::string_view encoded)603 util::StatusOr<SslUniquePtr<EC_POINT>> EcPointDecode(
604 EllipticCurveType curve, EcPointFormat format, absl::string_view encoded) {
605 switch (format) {
606 case EcPointFormat::UNCOMPRESSED:
607 case EcPointFormat::COMPRESSED:
608 return SslGetEcPointFromEncoded(curve, format, encoded);
609 case EcPointFormat::DO_NOT_USE_CRUNCHY_UNCOMPRESSED: {
610 util::StatusOr<SslUniquePtr<EC_GROUP>> group =
611 EcGroupFromCurveType(curve);
612 if (!group.ok()) {
613 return group.status();
614 }
615 const int kCurveSizeInBytes = SslEcFieldSizeInBytes(group->get());
616 if (encoded.size() != 2 * kCurveSizeInBytes) {
617 return util::Status(
618 absl::StatusCode::kInternal,
619 absl::StrCat("Encoded point's size is ", encoded.size(),
620 " bytes; expected ", 2 * kCurveSizeInBytes));
621 }
622 // SslGetEcPoint already checks if the point is on curve so we can return
623 // directly.
624 return SslGetEcPointFromCoordinates(group->get(),
625 encoded.substr(0, kCurveSizeInBytes),
626 encoded.substr(kCurveSizeInBytes));
627 }
628 default:
629 return util::Status(absl::StatusCode::kInternal, "Unsupported format");
630 }
631 }
632
EcGroupFromCurveType(EllipticCurveType curve_type)633 util::StatusOr<SslUniquePtr<EC_GROUP>> EcGroupFromCurveType(
634 EllipticCurveType curve_type) {
635 EC_GROUP *ec_group = nullptr;
636 switch (curve_type) {
637 case EllipticCurveType::NIST_P256: {
638 ec_group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
639 break;
640 }
641 case EllipticCurveType::NIST_P384: {
642 ec_group = EC_GROUP_new_by_curve_name(NID_secp384r1);
643 break;
644 }
645 case EllipticCurveType::NIST_P521: {
646 ec_group = EC_GROUP_new_by_curve_name(NID_secp521r1);
647 break;
648 }
649 default:
650 return util::Status(absl::StatusCode::kUnimplemented,
651 "Unsupported elliptic curve");
652 }
653 if (ec_group == nullptr) {
654 return util::Status(absl::StatusCode::kInternal,
655 "EC_GROUP_new_by_curve_name failed");
656 }
657 return {SslUniquePtr<EC_GROUP>(ec_group)};
658 }
659
CurveTypeFromEcGroup(const EC_GROUP * group)660 util::StatusOr<EllipticCurveType> CurveTypeFromEcGroup(const EC_GROUP *group) {
661 if (group == nullptr) {
662 return util::Status(absl::StatusCode::kInvalidArgument,
663 "Null group provided");
664 }
665 switch (EC_GROUP_get_curve_name(group)) {
666 case NID_X9_62_prime256v1:
667 return EllipticCurveType::NIST_P256;
668 case NID_secp384r1:
669 return EllipticCurveType::NIST_P384;
670 case NID_secp521r1:
671 return EllipticCurveType::NIST_P521;
672 default:
673 return util::Status(absl::StatusCode::kUnimplemented,
674 "Unsupported elliptic curve");
675 }
676 }
677
GetEcPoint(EllipticCurveType curve,absl::string_view pubx,absl::string_view puby)678 util::StatusOr<SslUniquePtr<EC_POINT>> GetEcPoint(EllipticCurveType curve,
679 absl::string_view pubx,
680 absl::string_view puby) {
681 util::StatusOr<SslUniquePtr<EC_GROUP>> group = EcGroupFromCurveType(curve);
682 if (!group.ok()) {
683 return group.status();
684 }
685 return SslGetEcPointFromCoordinates(group->get(), pubx, puby);
686 }
687
ComputeEcdhSharedSecret(EllipticCurveType curve,const BIGNUM * priv_key,const EC_POINT * pub_key)688 util::StatusOr<util::SecretData> ComputeEcdhSharedSecret(
689 EllipticCurveType curve, const BIGNUM *priv_key, const EC_POINT *pub_key) {
690 util::StatusOr<internal::SslUniquePtr<EC_GROUP>> priv_group =
691 internal::EcGroupFromCurveType(curve);
692 if (!priv_group.ok()) {
693 return priv_group.status();
694 }
695 if (EC_POINT_is_on_curve(priv_group->get(), pub_key, /*ctx=*/nullptr) != 1) {
696 return util::Status(absl::StatusCode::kInternal,
697 absl::StrCat("Public key is not on curve ",
698 subtle::EnumToString(curve)));
699 }
700
701 // Compute the shared point and make sure it is on `curve`.
702 internal::SslUniquePtr<EC_POINT> shared_point(
703 EC_POINT_new(priv_group->get()));
704 if (EC_POINT_mul(priv_group->get(), shared_point.get(), /*n=*/nullptr,
705 pub_key, priv_key, /*ctx=*/nullptr) != 1) {
706 return util::Status(absl::StatusCode::kInternal,
707 "Point multiplication failed");
708 }
709 if (EC_POINT_is_on_curve(priv_group->get(), shared_point.get(),
710 /*ctx=*/nullptr) != 1) {
711 return util::Status(absl::StatusCode::kInternal,
712 absl::StrCat("Shared point is not on curve ",
713 subtle::EnumToString(curve)));
714 }
715
716 util::StatusOr<EcPointCoordinates> shared_point_coordinates =
717 SslGetEcPointCoordinates(priv_group->get(), shared_point.get());
718 if (!shared_point_coordinates.ok()) {
719 return shared_point_coordinates.status();
720 }
721
722 // We need only the x coordinate.
723 return internal::BignumToSecretData(shared_point_coordinates->x.get(),
724 SslEcFieldSizeInBytes(priv_group->get()));
725 }
726
EcSignatureIeeeToDer(const EC_GROUP * group,absl::string_view ieee_sig)727 util::StatusOr<std::string> EcSignatureIeeeToDer(const EC_GROUP *group,
728 absl::string_view ieee_sig) {
729 const size_t kFieldSizeInBytes = SslEcFieldSizeInBytes(group);
730 if (ieee_sig.size() != kFieldSizeInBytes * 2) {
731 return util::Status(absl::StatusCode::kInvalidArgument,
732 "Signature is not valid.");
733 }
734 util::StatusOr<SslUniquePtr<BIGNUM>> r =
735 internal::StringToBignum(ieee_sig.substr(0, kFieldSizeInBytes));
736 if (!r.ok()) {
737 return r.status();
738 }
739 util::StatusOr<SslUniquePtr<BIGNUM>> s =
740 internal::StringToBignum(ieee_sig.substr(kFieldSizeInBytes));
741 if (!s.ok()) {
742 return s.status();
743 }
744 internal::SslUniquePtr<ECDSA_SIG> ecdsa(ECDSA_SIG_new());
745 if (ECDSA_SIG_set0(ecdsa.get(), r->get(), s->get()) != 1) {
746 return util::Status(absl::StatusCode::kInternal, "ECDSA_SIG_set0 failed");
747 }
748 // ECDSA_SIG_set0 takes ownership of s and r's pointers.
749 r->release();
750 s->release();
751
752 return SslEcdsaSignatureToBytes(ecdsa.get());
753 }
754
755 } // namespace internal
756 } // namespace tink
757 } // namespace crypto
758