xref: /aosp_15_r20/external/federated-compute/fcp/secagg/shared/ecdh_key_agreement.cc (revision 14675a029014e728ec732f129a32e299b2da0601)
1 /*
2  * Copyright 2018 Google LLC
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "fcp/secagg/shared/ecdh_key_agreement.h"
18 
19 #include <memory>
20 #include <string>
21 
22 #include "fcp/base/monitoring.h"
23 #include "fcp/secagg/shared/aes_key.h"
24 #include "fcp/secagg/shared/ecdh_keys.h"
25 #include "openssl/bn.h"
26 #include "openssl/ec.h"
27 #include "openssl/ecdh.h"
28 #include "openssl/evp.h"
29 #include "openssl/mem.h"
30 #include "openssl/sha.h"
31 
32 namespace fcp {
33 namespace secagg {
34 
EcdhKeyAgreement()35 EcdhKeyAgreement::EcdhKeyAgreement() : key_(nullptr, EC_KEY_free) {}
36 
EcdhKeyAgreement(EC_KEY * key)37 EcdhKeyAgreement::EcdhKeyAgreement(EC_KEY* key) : key_(key, EC_KEY_free) {}
38 
39 StatusOr<std::unique_ptr<EcdhKeyAgreement>>
CreateFromRandomKeys()40 EcdhKeyAgreement::CreateFromRandomKeys() {
41   EC_KEY* key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
42   EC_KEY_generate_key(key);
43   if (EC_KEY_check_key(key)) {
44     return std::make_unique<EcdhKeyAgreement>(key);
45   } else {
46     return FCP_STATUS(INTERNAL);
47   }
48 }
49 
50 StatusOr<std::unique_ptr<EcdhKeyAgreement>>
CreateFromPrivateKey(const EcdhPrivateKey & private_key)51 EcdhKeyAgreement::CreateFromPrivateKey(const EcdhPrivateKey& private_key) {
52   if (private_key.size() != EcdhPrivateKey::kSize) {
53     return FCP_STATUS(INVALID_ARGUMENT)
54            << "Private key must be of length " << EcdhPrivateKey::kSize;
55   }
56   // Wrap a raw pointer in a unique_ptr with a deleter to guarantee deletion.
57   BIGNUM* private_key_bn_raw =
58       BN_bin2bn(private_key.data(), private_key.size(), nullptr);
59   std::unique_ptr<BIGNUM, void (*)(BIGNUM*)> private_key_bn(private_key_bn_raw,
60                                                             BN_free);
61   EC_KEY* key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
62   if (!EC_KEY_set_private_key(key, private_key_bn.get())) {
63     return FCP_STATUS(INVALID_ARGUMENT) << "Invalid private key.";
64   }
65   return std::make_unique<EcdhKeyAgreement>(key);
66 }
67 
CreateFromKeypair(const EcdhPrivateKey & private_key,const EcdhPublicKey & public_key)68 StatusOr<std::unique_ptr<EcdhKeyAgreement>> EcdhKeyAgreement::CreateFromKeypair(
69     const EcdhPrivateKey& private_key, const EcdhPublicKey& public_key) {
70   if (public_key.size() != EcdhPublicKey::kSize &&
71       public_key.size() != EcdhPublicKey::kUncompressedSize) {
72     return FCP_STATUS(INVALID_ARGUMENT)
73            << "Public key must be of length " << EcdhPublicKey::kSize << " or "
74            << EcdhPublicKey::kUncompressedSize;
75   }
76 
77   // Create using a private key only first, then add the public key if it
78   // is valid.
79   auto ecdh = CreateFromPrivateKey(private_key);
80   if (!ecdh.ok()) {
81     return ecdh;
82   }
83 
84   // Copy this raw pointer for simplicity.
85   EC_KEY* key_ptr = ecdh.value()->key_.get();
86 
87   // Wrap a raw pointer in a unique_ptr with a deleter to guarantee deletion.
88   EC_POINT* public_key_point_raw = EC_POINT_new(EC_KEY_get0_group(key_ptr));
89   std::unique_ptr<EC_POINT, void (*)(EC_POINT*)> public_key_point(
90       public_key_point_raw, EC_POINT_free);
91 
92   if (!EC_POINT_oct2point(EC_KEY_get0_group(key_ptr), public_key_point.get(),
93                           public_key.data(), public_key.size(), nullptr)) {
94     return FCP_STATUS(INVALID_ARGUMENT) << "Invalid public key.";
95   }
96 
97   // This makes a copy of the public key, so deletion is safe.
98   if (!EC_KEY_set_public_key(key_ptr, public_key_point.get())) {
99     return FCP_STATUS(INVALID_ARGUMENT) << "Invalid public key.";
100   }
101 
102   if (EC_KEY_check_key(key_ptr)) {
103     return ecdh;
104   } else {
105     return FCP_STATUS(INVALID_ARGUMENT) << "Invalid keypair.";
106   }
107 }
108 
PrivateKey() const109 EcdhPrivateKey EcdhKeyAgreement::PrivateKey() const {
110   const BIGNUM* private_key_bn = EC_KEY_get0_private_key(key_.get());
111   uint8_t private_key[EcdhPrivateKey::kSize];
112   FCP_CHECK(
113       BN_bn2bin_padded(private_key, EcdhPrivateKey::kSize, private_key_bn));
114   return EcdhPrivateKey(private_key);
115 }
116 
PublicKey() const117 EcdhPublicKey EcdhKeyAgreement::PublicKey() const {
118   const EC_POINT* public_key_point = EC_KEY_get0_public_key(key_.get());
119   if (public_key_point == nullptr) {
120     return EcdhPublicKey();
121   }
122   uint8_t public_key[EcdhPublicKey::kSize];
123   int public_key_size = EC_POINT_point2oct(
124       EC_KEY_get0_group(key_.get()), public_key_point,
125       POINT_CONVERSION_COMPRESSED, public_key, EcdhPublicKey::kSize, nullptr);
126   FCP_CHECK(public_key_size == EcdhPublicKey::kSize);
127   return EcdhPublicKey(public_key);
128 }
129 
ComputeSharedSecret(const EcdhPublicKey & other_key) const130 StatusOr<AesKey> EcdhKeyAgreement::ComputeSharedSecret(
131     const EcdhPublicKey& other_key) const {
132   if (other_key.size() != EcdhPublicKey::kSize &&
133       other_key.size() != EcdhPublicKey::kUncompressedSize) {
134     return FCP_STATUS(INVALID_ARGUMENT)
135            << "Public key must be of length " << EcdhPublicKey::kSize << " or "
136            << EcdhPublicKey::kUncompressedSize;
137   }
138   // Wrap a raw pointer in a unique_ptr with a deleter to guarantee deletion.
139   EC_POINT* other_point_raw = EC_POINT_new(EC_KEY_get0_group(key_.get()));
140   std::unique_ptr<EC_POINT, void (*)(EC_POINT*)> other_point(other_point_raw,
141                                                              EC_POINT_free);
142   if (!EC_POINT_oct2point(EC_KEY_get0_group(key_.get()), other_point.get(),
143                           other_key.data(), other_key.size(), nullptr)) {
144     return FCP_STATUS(INVALID_ARGUMENT) << "Invalid ECDH public key.";
145   }
146   uint8_t secret[AesKey::kSize];
147   ECDH_compute_key(secret, AesKey::kSize, other_point.get(), key_.get(),
148                    nullptr);
149   return AesKey(secret);
150 }
151 
152 }  // namespace secagg
153 }  // namespace fcp
154