xref: /aosp_15_r20/external/tink/cc/internal/ec_util.cc (revision e7b1675dde1b92d52ec075b0a92829627f2c52a5)
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