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 * https://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/client/secagg_client_r1_share_keys_base_state.h"
18
19 #include <cstdint>
20 #include <string>
21 #include <utility>
22 #include <vector>
23
24 #include "fcp/secagg/client/other_client_state.h"
25 #include "fcp/secagg/client/secagg_client_alive_base_state.h"
26 #include "fcp/secagg/client/secagg_client_state.h"
27 #include "fcp/secagg/client/send_to_server_interface.h"
28 #include "fcp/secagg/client/state_transition_listener_interface.h"
29 #include "fcp/secagg/shared/aes_gcm_encryption.h"
30 #include "fcp/secagg/shared/compute_session_id.h"
31 #include "fcp/secagg/shared/ecdh_key_agreement.h"
32 #include "fcp/secagg/shared/prng.h"
33 #include "fcp/secagg/shared/secagg_messages.pb.h"
34 #include "fcp/secagg/shared/shamir_secret_sharing.h"
35
36 namespace fcp {
37 namespace secagg {
38
SecAggClientR1ShareKeysBaseState(std::unique_ptr<SendToServerInterface> sender,std::unique_ptr<StateTransitionListenerInterface> transition_listener,AsyncAbort * async_abort)39 SecAggClientR1ShareKeysBaseState::SecAggClientR1ShareKeysBaseState(
40 std::unique_ptr<SendToServerInterface> sender,
41 std::unique_ptr<StateTransitionListenerInterface> transition_listener,
42 AsyncAbort* async_abort)
43 : SecAggClientAliveBaseState(std::move(sender),
44 std::move(transition_listener),
45 ClientState::R1_SHARE_KEYS, async_abort) {}
46
SetUpShares(int threshold,int n,const Key & agreement_key,const Key & self_prng_key,std::vector<ShamirShare> * self_prng_key_shares,std::vector<ShamirShare> * pairwise_prng_key_shares)47 void SecAggClientR1ShareKeysBaseState::SetUpShares(
48 int threshold, int n, const Key& agreement_key, const Key& self_prng_key,
49 std::vector<ShamirShare>* self_prng_key_shares,
50 std::vector<ShamirShare>* pairwise_prng_key_shares) {
51 // This could be made into an assertion, but that would complicate the tests
52 // that call this method to get a "preview" of the shares.
53 if (pairwise_prng_key_shares->empty() && self_prng_key_shares->empty()) {
54 ShamirSecretSharing sharer;
55 *pairwise_prng_key_shares = sharer.Share(threshold, n, agreement_key);
56 *self_prng_key_shares = sharer.Share(threshold, n, self_prng_key);
57 }
58 }
59
HandleShareKeysRequest(const ShareKeysRequest & request,const EcdhKeyAgreement & enc_key_agreement,uint32_t max_neighbors_expected,uint32_t minimum_surviving_neighbors_for_reconstruction,const EcdhKeyAgreement & prng_key_agreement,const AesKey & self_prng_key,SecurePrng * prng,uint32_t * client_id,std::string * error_message,uint32_t * number_of_alive_clients,uint32_t * number_of_clients,std::vector<AesKey> * other_client_enc_keys,std::vector<AesKey> * other_client_prng_keys,std::vector<OtherClientState> * other_client_states,std::vector<ShamirShare> * self_prng_key_shares,std::vector<ShamirShare> * pairwise_prng_key_shares,SessionId * session_id)60 bool SecAggClientR1ShareKeysBaseState::HandleShareKeysRequest(
61 const ShareKeysRequest& request, const EcdhKeyAgreement& enc_key_agreement,
62 uint32_t max_neighbors_expected,
63 uint32_t minimum_surviving_neighbors_for_reconstruction,
64 const EcdhKeyAgreement& prng_key_agreement, const AesKey& self_prng_key,
65 SecurePrng* prng, uint32_t* client_id, std::string* error_message,
66 uint32_t* number_of_alive_clients, uint32_t* number_of_clients,
67 std::vector<AesKey>* other_client_enc_keys,
68 std::vector<AesKey>* other_client_prng_keys,
69 std::vector<OtherClientState>* other_client_states,
70 std::vector<ShamirShare>* self_prng_key_shares,
71 std::vector<ShamirShare>* pairwise_prng_key_shares, SessionId* session_id) {
72 transition_listener_->set_execution_session_id(
73 request.sec_agg_execution_logging_id());
74 if (request.pairs_of_public_keys().size() <
75 static_cast<int>(minimum_surviving_neighbors_for_reconstruction)) {
76 *error_message =
77 "The ShareKeysRequest received does not contain enough participants.";
78 return false;
79 } else if (request.pairs_of_public_keys().size() >
80 static_cast<int>(max_neighbors_expected)) {
81 *error_message =
82 "The ShareKeysRequest received contains too many participants.";
83 return false;
84 }
85
86 *number_of_alive_clients = request.pairs_of_public_keys().size();
87 *number_of_clients = request.pairs_of_public_keys().size();
88 bool client_id_set = false;
89
90 SetUpShares(minimum_surviving_neighbors_for_reconstruction,
91 *number_of_clients, prng_key_agreement.PrivateKey(),
92 self_prng_key, self_prng_key_shares, pairwise_prng_key_shares);
93
94 if (request.session_id().size() != kSha256Length) {
95 *error_message =
96 "Session ID is absent in ShareKeysRequest or has an unexpected length.";
97 return false;
98 }
99 session_id->data = request.session_id();
100
101 other_client_states->resize(*number_of_clients, OtherClientState::kAlive);
102 other_client_enc_keys->reserve(*number_of_clients);
103 other_client_prng_keys->reserve(*number_of_clients);
104
105 EcdhPublicKey self_enc_public_key = enc_key_agreement.PublicKey();
106 EcdhPublicKey self_prng_public_key = prng_key_agreement.PublicKey();
107
108 for (uint32_t i = 0; i < *number_of_clients; ++i) {
109 if (async_abort_ && async_abort_->Signalled()) {
110 *error_message = async_abort_->Message();
111 return false;
112 }
113 const PairOfPublicKeys& keys = request.pairs_of_public_keys(i);
114 if (keys.enc_pk().empty() || keys.noise_pk().empty()) {
115 // This is an aborted client, or it sent invalid keys.
116 other_client_states->at(i) = OtherClientState::kDeadAtRound1;
117 --(*number_of_alive_clients);
118 other_client_enc_keys->push_back(AesKey());
119 other_client_prng_keys->push_back(AesKey());
120 } else if (keys.enc_pk().size() != EcdhPublicKey::kSize ||
121 keys.noise_pk().size() != EcdhPublicKey::kSize) {
122 // The server forwarded an invalid public key.
123 *error_message = "Invalid public key in request from server.";
124 return false;
125 } else {
126 EcdhPublicKey enc_pk(
127 reinterpret_cast<const uint8_t*>(keys.enc_pk().data()));
128 EcdhPublicKey prng_pk(
129 reinterpret_cast<const uint8_t*>(keys.noise_pk().data()));
130 if (enc_pk == self_enc_public_key && prng_pk == self_prng_public_key) {
131 // This is this client.
132 if (client_id_set) {
133 *error_message =
134 "Found this client's keys in the ShareKeysRequest twice somehow.";
135 return false;
136 }
137 *client_id = i;
138 client_id_set = true;
139 // Add empty entries for own id.
140 other_client_enc_keys->push_back(AesKey());
141 other_client_prng_keys->push_back(AesKey());
142 } else {
143 auto shared_enc_key = enc_key_agreement.ComputeSharedSecret(enc_pk);
144 auto shared_prng_key = prng_key_agreement.ComputeSharedSecret(prng_pk);
145 if (!shared_enc_key.ok() || !shared_prng_key.ok()) {
146 // The server forwarded an invalid public key.
147 *error_message = "Invalid public key in request from server.";
148 return false;
149 }
150 other_client_enc_keys->push_back(shared_enc_key.value());
151 other_client_prng_keys->push_back(shared_prng_key.value());
152 }
153 }
154 }
155
156 if (*number_of_alive_clients <
157 minimum_surviving_neighbors_for_reconstruction) {
158 *error_message =
159 "There are not enough clients to complete this protocol session. "
160 "Aborting.";
161 return false;
162 }
163 if (!client_id_set) {
164 *error_message =
165 "The ShareKeysRequest sent by the server doesn't contain this client's "
166 "public keys.";
167 return false;
168 }
169 *error_message = "";
170 return true;
171 }
172
EncryptAndSendResponse(const std::vector<AesKey> & other_client_enc_keys,const std::vector<ShamirShare> & pairwise_prng_key_shares,const std::vector<ShamirShare> & self_prng_key_shares,SendToServerInterface * sender)173 bool SecAggClientR1ShareKeysBaseState::EncryptAndSendResponse(
174 const std::vector<AesKey>& other_client_enc_keys,
175 const std::vector<ShamirShare>& pairwise_prng_key_shares,
176 const std::vector<ShamirShare>& self_prng_key_shares,
177 SendToServerInterface* sender) {
178 ClientToServerWrapperMessage message;
179 ShareKeysResponse* response = message.mutable_share_keys_response();
180 AesGcmEncryption encryptor;
181
182 for (uint32_t i = 0; i < other_client_enc_keys.size(); ++i) {
183 if (async_abort_ && async_abort_->Signalled()) return false;
184 if (other_client_enc_keys[i].size() == 0) {
185 // Add a blank for dropped-out clients and for this client.
186 response->add_encrypted_key_shares("");
187 } else {
188 PairOfKeyShares key_shares_pair;
189 key_shares_pair.set_noise_sk_share(pairwise_prng_key_shares[i].data);
190 key_shares_pair.set_prf_sk_share(self_prng_key_shares[i].data);
191 std::string serialized_pair = key_shares_pair.SerializeAsString();
192 response->add_encrypted_key_shares(
193 encryptor.Encrypt(other_client_enc_keys[i], serialized_pair));
194 }
195 }
196
197 sender->Send(&message);
198 return true;
199 }
200
201 } // namespace secagg
202 } // namespace fcp
203