1 // Copyright 2017 Google Inc.
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
17 #include "tink/subtle/ecies_hkdf_recipient_kem_boringssl.h"
18
19 #include <memory>
20 #include <utility>
21
22 #include "absl/memory/memory.h"
23 #include "absl/status/status.h"
24 #include "openssl/bn.h"
25 #include "openssl/ec.h"
26 #include "openssl/evp.h"
27 #include "tink/internal/ec_util.h"
28 #include "tink/internal/ssl_unique_ptr.h"
29 #include "tink/subtle/common_enums.h"
30 #include "tink/subtle/hkdf.h"
31 #include "tink/util/errors.h"
32 #include "tink/util/secret_data.h"
33
34 namespace crypto {
35 namespace tink {
36 namespace subtle {
37
38 // static
39 util::StatusOr<std::unique_ptr<EciesHkdfRecipientKemBoringSsl>>
New(EllipticCurveType curve,util::SecretData priv_key)40 EciesHkdfRecipientKemBoringSsl::New(EllipticCurveType curve,
41 util::SecretData priv_key) {
42 switch (curve) {
43 case EllipticCurveType::NIST_P256:
44 case EllipticCurveType::NIST_P384:
45 case EllipticCurveType::NIST_P521:
46 return EciesHkdfNistPCurveRecipientKemBoringSsl::New(curve,
47 std::move(priv_key));
48 case EllipticCurveType::CURVE25519:
49 return EciesHkdfX25519RecipientKemBoringSsl::New(curve,
50 std::move(priv_key));
51 default:
52 return util::Status(absl::StatusCode::kUnimplemented,
53 "Unsupported elliptic curve");
54 }
55 }
56
57 // static
58 util::StatusOr<std::unique_ptr<EciesHkdfRecipientKemBoringSsl>>
New(EllipticCurveType curve,util::SecretData priv_key)59 EciesHkdfNistPCurveRecipientKemBoringSsl::New(EllipticCurveType curve,
60 util::SecretData priv_key) {
61 auto status = internal::CheckFipsCompatibility<
62 EciesHkdfNistPCurveRecipientKemBoringSsl>();
63 if (!status.ok()) return status;
64
65 if (priv_key.empty()) {
66 return util::Status(absl::StatusCode::kInvalidArgument, "empty priv_key");
67 }
68 auto status_or_ec_group = internal::EcGroupFromCurveType(curve);
69 if (!status_or_ec_group.ok()) return status_or_ec_group.status();
70 // TODO(przydatek): consider refactoring internal/ec_util,
71 // so that the saved group can be used for KEM operations.
72 return {absl::WrapUnique(new EciesHkdfNistPCurveRecipientKemBoringSsl(
73 curve, std::move(priv_key), std::move(status_or_ec_group.value())))};
74 }
75
76 EciesHkdfNistPCurveRecipientKemBoringSsl::
EciesHkdfNistPCurveRecipientKemBoringSsl(EllipticCurveType curve,util::SecretData priv_key_value,internal::SslUniquePtr<EC_GROUP> ec_group)77 EciesHkdfNistPCurveRecipientKemBoringSsl(
78 EllipticCurveType curve, util::SecretData priv_key_value,
79 internal::SslUniquePtr<EC_GROUP> ec_group)
80 : curve_(curve),
81 priv_key_value_(std::move(priv_key_value)),
82 ec_group_(std::move(ec_group)) {}
83
84 util::StatusOr<util::SecretData>
GenerateKey(absl::string_view kem_bytes,HashType hash,absl::string_view hkdf_salt,absl::string_view hkdf_info,uint32_t key_size_in_bytes,EcPointFormat point_format) const85 EciesHkdfNistPCurveRecipientKemBoringSsl::GenerateKey(
86 absl::string_view kem_bytes, HashType hash, absl::string_view hkdf_salt,
87 absl::string_view hkdf_info, uint32_t key_size_in_bytes,
88 EcPointFormat point_format) const {
89 auto status_or_ec_point =
90 internal::EcPointDecode(curve_, point_format, kem_bytes);
91 if (!status_or_ec_point.ok()) {
92 return ToStatusF(absl::StatusCode::kInvalidArgument,
93 "Invalid KEM bytes: %s",
94 status_or_ec_point.status().message());
95 }
96 internal::SslUniquePtr<EC_POINT> pub_key =
97 std::move(status_or_ec_point.value());
98 internal::SslUniquePtr<BIGNUM> priv_key(
99 BN_bin2bn(priv_key_value_.data(), priv_key_value_.size(), nullptr));
100 auto shared_secret_or =
101 internal::ComputeEcdhSharedSecret(curve_, priv_key.get(), pub_key.get());
102 if (!shared_secret_or.ok()) {
103 return shared_secret_or.status();
104 }
105 util::SecretData shared_secret = shared_secret_or.value();
106 return Hkdf::ComputeEciesHkdfSymmetricKey(
107 hash, kem_bytes, shared_secret, hkdf_salt, hkdf_info, key_size_in_bytes);
108 }
109
EciesHkdfX25519RecipientKemBoringSsl(internal::SslUniquePtr<EVP_PKEY> private_key)110 EciesHkdfX25519RecipientKemBoringSsl::EciesHkdfX25519RecipientKemBoringSsl(
111 internal::SslUniquePtr<EVP_PKEY> private_key)
112 : private_key_(std::move(private_key)) {}
113
114 // static
115 util::StatusOr<std::unique_ptr<EciesHkdfRecipientKemBoringSsl>>
New(EllipticCurveType curve,util::SecretData priv_key)116 EciesHkdfX25519RecipientKemBoringSsl::New(EllipticCurveType curve,
117 util::SecretData priv_key) {
118 auto status =
119 internal::CheckFipsCompatibility<EciesHkdfX25519RecipientKemBoringSsl>();
120 if (!status.ok()) return status;
121
122 if (curve != CURVE25519) {
123 return util::Status(absl::StatusCode::kInvalidArgument,
124 "curve is not CURVE25519");
125 }
126 if (priv_key.size() != internal::X25519KeyPubKeySize()) {
127 return util::Status(absl::StatusCode::kInvalidArgument,
128 "pubx has unexpected length");
129 }
130
131 internal::SslUniquePtr<EVP_PKEY> ssl_priv_key(EVP_PKEY_new_raw_private_key(
132 /*type=*/EVP_PKEY_X25519, /*unused=*/nullptr, /*in=*/priv_key.data(),
133 /*len=*/internal::Ed25519KeyPrivKeySize()));
134 if (ssl_priv_key == nullptr) {
135 return util::Status(absl::StatusCode::kInternal,
136 "EVP_PKEY_new_raw_private_key failed");
137 }
138
139 return {absl::WrapUnique(
140 new EciesHkdfX25519RecipientKemBoringSsl(std::move(ssl_priv_key)))};
141 }
142
143 crypto::tink::util::StatusOr<util::SecretData>
GenerateKey(absl::string_view kem_bytes,HashType hash,absl::string_view hkdf_salt,absl::string_view hkdf_info,uint32_t key_size_in_bytes,EcPointFormat point_format) const144 EciesHkdfX25519RecipientKemBoringSsl::GenerateKey(
145 absl::string_view kem_bytes, HashType hash, absl::string_view hkdf_salt,
146 absl::string_view hkdf_info, uint32_t key_size_in_bytes,
147 EcPointFormat point_format) const {
148 if (point_format != EcPointFormat::COMPRESSED) {
149 return util::Status(
150 absl::StatusCode::kInvalidArgument,
151 "X25519 only supports compressed elliptic curve points");
152 }
153
154 if (kem_bytes.size() != internal::X25519KeyPubKeySize()) {
155 return util::Status(absl::StatusCode::kInvalidArgument,
156 "kem_bytes has unexpected size");
157 }
158
159 internal::SslUniquePtr<EVP_PKEY> peer_key(EVP_PKEY_new_raw_public_key(
160 /*type=*/EVP_PKEY_X25519, /*unused=*/nullptr,
161 /*in=*/reinterpret_cast<const uint8_t*>(kem_bytes.data()),
162 /*len=*/internal::Ed25519KeyPubKeySize()));
163 if (peer_key == nullptr) {
164 return util::Status(absl::StatusCode::kInternal,
165 "EVP_PKEY_new_raw_public_key failed");
166 }
167
168 util::StatusOr<util::SecretData> shared_secret =
169 internal::ComputeX25519SharedSecret(private_key_.get(), peer_key.get());
170 if (!shared_secret.ok()) {
171 return shared_secret.status();
172 }
173
174 return Hkdf::ComputeEciesHkdfSymmetricKey(
175 hash, kem_bytes, *shared_secret, hkdf_salt, hkdf_info, key_size_in_bytes);
176 }
177
178 } // namespace subtle
179 } // namespace tink
180 } // namespace crypto
181