Skip to content
This repository was archived by the owner on Apr 17, 2024. It is now read-only.

Commit 069da25

Browse files
willinoiscopybara-github
authored andcommitted
Add ECIES private key.
PiperOrigin-RevId: 602859487
1 parent d36b83a commit 069da25

5 files changed

+894
-0
lines changed

cc/hybrid/BUILD.bazel

+63
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,37 @@ cc_library(
479479
],
480480
)
481481

482+
cc_library(
483+
name = "ecies_private_key",
484+
srcs = ["ecies_private_key.cc"],
485+
hdrs = ["ecies_private_key.h"],
486+
include_prefix = "tink/hybrid",
487+
deps = [
488+
":ecies_parameters",
489+
":ecies_public_key",
490+
":hybrid_private_key",
491+
"//:ec_point",
492+
"//:insecure_secret_key_access",
493+
"//:key",
494+
"//:partial_key_access_token",
495+
"//:restricted_big_integer",
496+
"//:restricted_data",
497+
"//internal:bn_util",
498+
"//internal:ec_util",
499+
"//internal:err_util",
500+
"//internal:ssl_unique_ptr",
501+
"//subtle:common_enums",
502+
"//util:secret_data",
503+
"//util:status",
504+
"//util:statusor",
505+
"@boringssl//:crypto",
506+
"@com_google_absl//absl/status",
507+
"@com_google_absl//absl/strings",
508+
"@com_google_absl//absl/strings:str_format",
509+
"@com_google_absl//absl/types:optional",
510+
],
511+
)
512+
482513
# tests
483514

484515
cc_test(
@@ -910,16 +941,48 @@ cc_test(
910941
":ecies_public_key",
911942
"//:big_integer",
912943
"//:ec_point",
944+
"//:parameters",
913945
"//:partial_key_access",
946+
"//daead:aes_siv_parameters",
914947
"//internal:ec_util",
948+
"//internal:ssl_unique_ptr",
915949
"//subtle:common_enums",
916950
"//subtle:random",
917951
"//util:statusor",
918952
"//util:test_matchers",
919953
"@boringssl//:crypto",
954+
"@com_google_absl//absl/memory",
920955
"@com_google_absl//absl/status",
921956
"@com_google_absl//absl/strings",
922957
"@com_google_absl//absl/types:optional",
923958
"@com_google_googletest//:gtest_main",
924959
],
925960
)
961+
962+
cc_test(
963+
name = "ecies_private_key_test",
964+
srcs = ["ecies_private_key_test.cc"],
965+
deps = [
966+
":ecies_parameters",
967+
":ecies_private_key",
968+
":ecies_public_key",
969+
"//:big_integer",
970+
"//:ec_point",
971+
"//:insecure_secret_key_access",
972+
"//:partial_key_access",
973+
"//:restricted_big_integer",
974+
"//:restricted_data",
975+
"//internal:ec_util",
976+
"//subtle:common_enums",
977+
"//subtle:random",
978+
"//util:secret_data",
979+
"//util:statusor",
980+
"//util:test_matchers",
981+
"@boringssl//:crypto",
982+
"@com_google_absl//absl/status",
983+
"@com_google_absl//absl/strings",
984+
"@com_google_absl//absl/strings:string_view",
985+
"@com_google_absl//absl/types:optional",
986+
"@com_google_googletest//:gtest_main",
987+
],
988+
)

cc/hybrid/CMakeLists.txt

+62
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,36 @@ tink_cc_library(
478478
tink::util::statusor
479479
)
480480

481+
tink_cc_library(
482+
NAME ecies_private_key
483+
SRCS
484+
ecies_private_key.cc
485+
ecies_private_key.h
486+
DEPS
487+
tink::hybrid::ecies_parameters
488+
tink::hybrid::ecies_public_key
489+
tink::hybrid::hybrid_private_key
490+
absl::status
491+
absl::strings
492+
absl::str_format
493+
absl::optional
494+
crypto
495+
tink::core::ec_point
496+
tink::core::insecure_secret_key_access
497+
tink::core::key
498+
tink::core::partial_key_access_token
499+
tink::core::restricted_big_integer
500+
tink::core::restricted_data
501+
tink::internal::bn_util
502+
tink::internal::ec_util
503+
tink::internal::err_util
504+
tink::internal::ssl_unique_ptr
505+
tink::subtle::common_enums
506+
tink::util::secret_data
507+
tink::util::status
508+
tink::util::statusor
509+
)
510+
481511
# tests
482512

483513
tink_cc_test(
@@ -919,16 +949,48 @@ tink_cc_test(
919949
tink::hybrid::ecies_parameters
920950
tink::hybrid::ecies_public_key
921951
gmock
952+
absl::memory
953+
absl::status
954+
absl::strings
955+
absl::optional
956+
crypto
957+
tink::core::big_integer
958+
tink::core::ec_point
959+
tink::core::parameters
960+
tink::core::partial_key_access
961+
tink::daead::aes_siv_parameters
962+
tink::internal::ec_util
963+
tink::internal::ssl_unique_ptr
964+
tink::subtle::common_enums
965+
tink::subtle::random
966+
tink::util::statusor
967+
tink::util::test_matchers
968+
)
969+
970+
tink_cc_test(
971+
NAME ecies_private_key_test
972+
SRCS
973+
ecies_private_key_test.cc
974+
DEPS
975+
tink::hybrid::ecies_parameters
976+
tink::hybrid::ecies_private_key
977+
tink::hybrid::ecies_public_key
978+
gmock
922979
absl::status
923980
absl::strings
981+
absl::string_view
924982
absl::optional
925983
crypto
926984
tink::core::big_integer
927985
tink::core::ec_point
986+
tink::core::insecure_secret_key_access
928987
tink::core::partial_key_access
988+
tink::core::restricted_big_integer
989+
tink::core::restricted_data
929990
tink::internal::ec_util
930991
tink::subtle::common_enums
931992
tink::subtle::random
993+
tink::util::secret_data
932994
tink::util::statusor
933995
tink::util::test_matchers
934996
)

cc/hybrid/ecies_private_key.cc

+213
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
// Copyright 2024 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+
17+
#include "tink/hybrid/ecies_private_key.h"
18+
19+
#include <memory>
20+
21+
#include "absl/status/status.h"
22+
#include "absl/strings/str_cat.h"
23+
#include "absl/strings/str_format.h"
24+
#include "absl/strings/string_view.h"
25+
#include "absl/types/optional.h"
26+
#include "tink/ec_point.h"
27+
#include "tink/internal/bn_util.h"
28+
#include "tink/restricted_big_integer.h"
29+
#ifdef OPENSSL_IS_BORINGSSL
30+
#include "openssl/base.h"
31+
#include "openssl/ec_key.h"
32+
#else
33+
#include "openssl/ec.h"
34+
#endif
35+
#include "tink/hybrid/ecies_parameters.h"
36+
#include "tink/hybrid/ecies_public_key.h"
37+
#include "tink/insecure_secret_key_access.h"
38+
#include "tink/internal/ec_util.h"
39+
#include "tink/internal/err_util.h"
40+
#include "tink/internal/ssl_unique_ptr.h"
41+
#include "tink/key.h"
42+
#include "tink/partial_key_access_token.h"
43+
#include "tink/restricted_data.h"
44+
#include "tink/subtle/common_enums.h"
45+
#include "tink/util/secret_data.h"
46+
#include "tink/util/status.h"
47+
#include "tink/util/statusor.h"
48+
49+
namespace crypto {
50+
namespace tink {
51+
namespace {
52+
53+
util::StatusOr<subtle::EllipticCurveType> SubtleCurveType(
54+
EciesParameters::CurveType curve_type) {
55+
switch (curve_type) {
56+
case EciesParameters::CurveType::kNistP256:
57+
return subtle::EllipticCurveType::NIST_P256;
58+
case EciesParameters::CurveType::kNistP384:
59+
return subtle::EllipticCurveType::NIST_P384;
60+
case EciesParameters::CurveType::kNistP521:
61+
return subtle::EllipticCurveType::NIST_P521;
62+
case EciesParameters::CurveType::kX25519:
63+
return subtle::EllipticCurveType::CURVE25519;
64+
default:
65+
return util::Status(absl::StatusCode::kInvalidArgument,
66+
absl::StrCat("Unknown curve type: ", curve_type));
67+
}
68+
}
69+
70+
util::Status ValidateNistKeyPair(const EciesPublicKey& public_key,
71+
const RestrictedBigInteger& private_key_value,
72+
PartialKeyAccessToken token) {
73+
internal::SslUniquePtr<EC_KEY> key(EC_KEY_new());
74+
75+
// Set EC_KEY group.
76+
util::StatusOr<subtle::EllipticCurveType> curve =
77+
SubtleCurveType(public_key.GetParameters().GetCurveType());
78+
if (!curve.ok()) {
79+
return curve.status();
80+
}
81+
util::StatusOr<internal::SslUniquePtr<EC_GROUP>> group =
82+
internal::EcGroupFromCurveType(*curve);
83+
if (!group.ok()) {
84+
return group.status();
85+
}
86+
EC_KEY_set_group(key.get(), group->get());
87+
88+
// Set EC_KEY public key.
89+
absl::optional<EcPoint> ec_point = public_key.GetNistCurvePoint(token);
90+
if (!ec_point.has_value()) {
91+
return util::Status(absl::StatusCode::kInvalidArgument,
92+
"Missing public point for NIST curve public key.");
93+
}
94+
util::StatusOr<internal::SslUniquePtr<EC_POINT>> public_point =
95+
internal::GetEcPoint(*curve, ec_point->GetX().GetValue(),
96+
ec_point->GetY().GetValue());
97+
if (!public_point.ok()) {
98+
return public_point.status();
99+
}
100+
if (!EC_KEY_set_public_key(key.get(), public_point->get())) {
101+
return util::Status(
102+
absl::StatusCode::kInvalidArgument,
103+
absl::StrCat("Invalid public key: ", internal::GetSslErrors()));
104+
}
105+
106+
// Set EC_KEY private key.
107+
util::StatusOr<internal::SslUniquePtr<BIGNUM>> priv_big_num =
108+
internal::StringToBignum(
109+
private_key_value.GetSecret(InsecureSecretKeyAccess::Get()));
110+
if (!priv_big_num.ok()) {
111+
return priv_big_num.status();
112+
}
113+
if (!EC_KEY_set_private_key(key.get(), priv_big_num->get())) {
114+
return util::Status(
115+
absl::StatusCode::kInvalidArgument,
116+
absl::StrCat("Invalid private key: ", internal::GetSslErrors()));
117+
}
118+
119+
// Check that EC_KEY is valid.
120+
if (!EC_KEY_check_key(key.get())) {
121+
return util::Status(
122+
absl::StatusCode::kInvalidArgument,
123+
absl::StrCat("Invalid EC key pair: ", internal::GetSslErrors()));
124+
}
125+
126+
return util::OkStatus();
127+
}
128+
129+
util::Status ValidateX25519KeyPair(const EciesPublicKey& public_key,
130+
const RestrictedData& private_key_bytes,
131+
PartialKeyAccessToken token) {
132+
util::StatusOr<std::unique_ptr<internal::X25519Key>> x25519_key =
133+
internal::X25519KeyFromPrivateKey(util::SecretDataFromStringView(
134+
private_key_bytes.GetSecret(InsecureSecretKeyAccess::Get())));
135+
if (!x25519_key.ok()) {
136+
return x25519_key.status();
137+
}
138+
139+
absl::optional<absl::string_view> public_key_bytes =
140+
public_key.GetX25519CurvePointBytes(token);
141+
if (!public_key_bytes.has_value()) {
142+
return util::Status(absl::StatusCode::kInvalidArgument,
143+
"Missing public key bytes for X25519 public key.");
144+
}
145+
146+
absl::string_view public_key_bytes_from_private = absl::string_view(
147+
reinterpret_cast<const char*>((*x25519_key)->public_value),
148+
internal::X25519KeyPubKeySize());
149+
150+
if (public_key_bytes != public_key_bytes_from_private) {
151+
return util::Status(
152+
absl::StatusCode::kInvalidArgument,
153+
"X25519 private key does not match the specified X25519 public key.");
154+
}
155+
156+
return util::OkStatus();
157+
}
158+
159+
} // namespace
160+
161+
util::StatusOr<EciesPrivateKey> EciesPrivateKey::CreateForNistCurve(
162+
const EciesPublicKey& public_key,
163+
const RestrictedBigInteger& private_key_value,
164+
PartialKeyAccessToken token) {
165+
// Validate that public and private key match.
166+
util::Status key_pair_validation =
167+
ValidateNistKeyPair(public_key, private_key_value, token);
168+
if (!key_pair_validation.ok()) {
169+
return key_pair_validation;
170+
}
171+
return EciesPrivateKey(public_key, private_key_value);
172+
}
173+
174+
util::StatusOr<EciesPrivateKey> EciesPrivateKey::CreateForCurveX25519(
175+
const EciesPublicKey& public_key, const RestrictedData& private_key_bytes,
176+
PartialKeyAccessToken token) {
177+
// Validate private key length.
178+
int private_key_length =
179+
private_key_bytes.GetSecret(InsecureSecretKeyAccess::Get()).length();
180+
if (private_key_length != internal::X25519KeyPrivKeySize()) {
181+
return util::Status(
182+
absl::StatusCode::kInvalidArgument,
183+
absl::StrFormat(
184+
"Invalid X25519 private key length (expected %d, got %d)",
185+
internal::X25519KeyPrivKeySize(), private_key_length));
186+
}
187+
188+
// Validate that public and private key match.
189+
util::Status key_pair_validation =
190+
ValidateX25519KeyPair(public_key, private_key_bytes, token);
191+
if (!key_pair_validation.ok()) {
192+
return key_pair_validation;
193+
}
194+
195+
return EciesPrivateKey(public_key, private_key_bytes);
196+
}
197+
198+
bool EciesPrivateKey::operator==(const Key& other) const {
199+
const EciesPrivateKey* that = dynamic_cast<const EciesPrivateKey*>(&other);
200+
if (that == nullptr) {
201+
return false;
202+
}
203+
if (public_key_ != that->public_key_) {
204+
return false;
205+
}
206+
if (private_key_value_ != that->private_key_value_) {
207+
return false;
208+
}
209+
return private_key_bytes_ == that->private_key_bytes_;
210+
}
211+
212+
} // namespace tink
213+
} // namespace crypto

0 commit comments

Comments
 (0)