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