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