• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 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 //    https://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 #include "anonymous_tokens/cpp/client/anonymous_tokens_redemption_client.h"
16 
17 #include <cstdint>
18 #include <memory>
19 #include <random>
20 #include <string>
21 #include <utility>
22 #include <vector>
23 
24 #include <gmock/gmock.h>
25 #include <gtest/gtest.h>
26 #include "anonymous_tokens/cpp/testing/utils.h"
27 #include "anonymous_tokens/proto/anonymous_tokens.pb.h"
28 
29 
30 namespace anonymous_tokens {
31 namespace {
32 
33 using ::testing::HasSubstr;
34 
35 // Generates a random string of size n.
GetRandomString(int n,std::uniform_int_distribution<int> * distr_u8,std::mt19937_64 * generator)36 std::string GetRandomString(int n, std::uniform_int_distribution<int>* distr_u8,
37                             std::mt19937_64* generator) {
38   std::string rand(n, 0);
39   for (int i = 0; i < n; ++i) {
40     rand[i] = static_cast<uint8_t>((*distr_u8)(*generator));
41   }
42   return rand;
43 }
44 
45 // Saves redemption related public metadata and result for testing purposes for
46 // one token.
47 struct RedemptionInfoAndResult {
48   std::string plaintext_message;
49   std::string public_metadata;
50   std::string message_mask;
51   bool redeemed;
52   bool double_spent;
53 };
54 
55 // Takes as input AnonymousTokensRedemptionResponse and uses that to create a
56 // map of token to their respective RedemptionResult for testing purposes.
57 absl::flat_hash_map<std::string, RedemptionInfoAndResult>
CreateTokenToRedemptionResultMap(const AnonymousTokensRedemptionResponse & response)58 CreateTokenToRedemptionResultMap(
59     const AnonymousTokensRedemptionResponse& response) {
60   absl::flat_hash_map<std::string, RedemptionInfoAndResult> response_map;
61   for (const auto& result : response.anonymous_token_redemption_results()) {
62     response_map[result.serialized_unblinded_token()] = {
63         .plaintext_message = result.plaintext_message(),
64         .public_metadata = result.public_metadata(),
65         .message_mask = result.message_mask(),
66         .redeemed = result.verified(),
67         .double_spent = result.double_spent(),
68     };
69   }
70   return response_map;
71 }
72 
73 class AnonymousTokensRedemptionClientTest : public testing::Test {
74  protected:
SetUp()75   void SetUp() override {
76     ANON_TOKENS_ASSERT_OK_AND_ASSIGN(
77         client_, AnonymousTokensRedemptionClient::Create(TEST_USE_CASE, 1));
78     dummy_token_with_input_ = GetRandomDummyTokenWithInput();
79     *(dummy_response_.add_anonymous_token_redemption_results()) =
80         CreateRedemptionResultForTesting(dummy_token_with_input_);
81     generator_.seed(GTEST_FLAG_GET(random_seed));
82   }
83 
84   // Generates a dummy RSABlindSignatureTokenWithInput which is not
85   // cryptographically valid as that is not needed for testing purposes.
GetRandomDummyTokenWithInput()86   RSABlindSignatureTokenWithInput GetRandomDummyTokenWithInput() {
87     PlaintextMessageWithPublicMetadata input;
88     input.set_plaintext_message(GetRandomString(20, &distr_u8_, &generator_));
89     input.set_public_metadata(GetRandomString(10, &distr_u8_, &generator_));
90     RSABlindSignatureToken token;
91     token.set_token(GetRandomString(512, &distr_u8_, &generator_));
92     token.set_message_mask(GetRandomString(32, &distr_u8_, &generator_));
93     RSABlindSignatureTokenWithInput token_with_input;
94     *token_with_input.mutable_input() = input;
95     *token_with_input.mutable_token() = token;
96     return token_with_input;
97   }
98 
99   // Creates a fake token redemption response for one
100   // RSABlindSignatureTokenWithInput and outputs it as
101   // AnonymousTokenRedemptionResult
102   AnonymousTokensRedemptionResponse_AnonymousTokenRedemptionResult
CreateRedemptionResultForTesting(RSABlindSignatureTokenWithInput token_with_input,bool verified=true,bool double_spent=false,AnonymousTokensUseCase use_case=TEST_USE_CASE,int64_t key_version=1)103   CreateRedemptionResultForTesting(
104       RSABlindSignatureTokenWithInput token_with_input, bool verified = true,
105       bool double_spent = false,
106       AnonymousTokensUseCase use_case = TEST_USE_CASE,
107       int64_t key_version = 1) {
108     AnonymousTokensRedemptionResponse_AnonymousTokenRedemptionResult result;
109     result.set_use_case(AnonymousTokensUseCase_Name(use_case));
110     result.set_key_version(key_version);
111     result.set_public_metadata(token_with_input.input().public_metadata());
112     result.set_serialized_unblinded_token(token_with_input.token().token());
113     result.set_plaintext_message(token_with_input.input().plaintext_message());
114     result.set_message_mask(token_with_input.token().message_mask());
115     result.set_verified(verified);
116     result.set_double_spent(double_spent);
117     return result;
118   }
119 
120   std::mt19937_64 generator_;
121   std::uniform_int_distribution<int> distr_u8_ =
122       std::uniform_int_distribution<int>{0, 255};
123 
124   std::unique_ptr<AnonymousTokensRedemptionClient> client_;
125   RSABlindSignatureTokenWithInput dummy_token_with_input_;
126   AnonymousTokensRedemptionResponse dummy_response_;
127 };
128 
TEST_F(AnonymousTokensRedemptionClientTest,UndefinedUseCase)129 TEST_F(AnonymousTokensRedemptionClientTest, UndefinedUseCase) {
130   // Use case undefined.
131   absl::StatusOr<std::unique_ptr<AnonymousTokensRedemptionClient>>
132       redemption_client = AnonymousTokensRedemptionClient::Create(
133           ANONYMOUS_TOKENS_USE_CASE_UNDEFINED, 1);
134   EXPECT_EQ(redemption_client.status().code(),
135             absl::StatusCode::kInvalidArgument);
136   EXPECT_THAT(redemption_client.status().message(),
137               HasSubstr("must be defined"));
138 }
139 
TEST_F(AnonymousTokensRedemptionClientTest,InvalidKeyVersions)140 TEST_F(AnonymousTokensRedemptionClientTest, InvalidKeyVersions) {
141   // Key version 0.
142   absl::StatusOr<std::unique_ptr<AnonymousTokensRedemptionClient>>
143       redemption_client_1 =
144           AnonymousTokensRedemptionClient::Create(TEST_USE_CASE, 0);
145   EXPECT_EQ(redemption_client_1.status().code(),
146             absl::StatusCode::kInvalidArgument);
147   EXPECT_THAT(redemption_client_1.status().message(),
148               HasSubstr("must be greater than 0"));
149   // Key version negative.
150   absl::StatusOr<std::unique_ptr<AnonymousTokensRedemptionClient>>
151       redemption_client_2 =
152           AnonymousTokensRedemptionClient::Create(TEST_USE_CASE, -10);
153   EXPECT_EQ(redemption_client_2.status().code(),
154             absl::StatusCode::kInvalidArgument);
155   EXPECT_THAT(redemption_client_2.status().message(),
156               HasSubstr("must be greater than 0"));
157 }
158 
TEST_F(AnonymousTokensRedemptionClientTest,EmptyRequest)159 TEST_F(AnonymousTokensRedemptionClientTest, EmptyRequest) {
160   absl::StatusOr<AnonymousTokensRedemptionRequest> redemption_request =
161       client_->CreateAnonymousTokensRedemptionRequest({});
162   EXPECT_EQ(redemption_request.status().code(),
163             absl::StatusCode::kInvalidArgument);
164   EXPECT_THAT(redemption_request.status().message(),
165               HasSubstr("empty request"));
166 }
167 
TEST_F(AnonymousTokensRedemptionClientTest,CreatingRequestAgain)168 TEST_F(AnonymousTokensRedemptionClientTest, CreatingRequestAgain) {
169   ANON_TOKENS_ASSERT_OK_AND_ASSIGN(
170       auto _, client_->CreateAnonymousTokensRedemptionRequest(
171                   {dummy_token_with_input_}));
172   // Creating same request again with same client.
173   absl::StatusOr<AnonymousTokensRedemptionRequest> redemption_request_2 =
174       client_->CreateAnonymousTokensRedemptionRequest(
175           {dummy_token_with_input_});
176   EXPECT_EQ(redemption_request_2.status().code(),
177             absl::StatusCode::kFailedPrecondition);
178   EXPECT_THAT(redemption_request_2.status().message(),
179               HasSubstr("already created"));
180   // Creating different request with the same client.
181   absl::StatusOr<AnonymousTokensRedemptionRequest> redemption_request_3 =
182       client_->CreateAnonymousTokensRedemptionRequest(
183           {GetRandomDummyTokenWithInput()});
184   EXPECT_EQ(redemption_request_3.status().code(),
185             absl::StatusCode::kFailedPrecondition);
186   EXPECT_THAT(redemption_request_3.status().message(),
187               HasSubstr("already created"));
188 }
189 
TEST_F(AnonymousTokensRedemptionClientTest,MissingTokenInRequest)190 TEST_F(AnonymousTokensRedemptionClientTest, MissingTokenInRequest) {
191   dummy_token_with_input_.mutable_token()->clear_token();
192   absl::StatusOr<AnonymousTokensRedemptionRequest> redemption_request =
193       client_->CreateAnonymousTokensRedemptionRequest(
194           {dummy_token_with_input_});
195   EXPECT_EQ(redemption_request.status().code(),
196             absl::StatusCode::kInvalidArgument);
197   EXPECT_THAT(redemption_request.status().message(), HasSubstr("empty token"));
198 }
199 
TEST_F(AnonymousTokensRedemptionClientTest,WrongMaskSize)200 TEST_F(AnonymousTokensRedemptionClientTest, WrongMaskSize) {
201   dummy_token_with_input_.mutable_token()->set_message_mask("wrongmasksize");
202   absl::StatusOr<AnonymousTokensRedemptionRequest> redemption_request =
203       client_->CreateAnonymousTokensRedemptionRequest(
204           {dummy_token_with_input_});
205   EXPECT_EQ(redemption_request.status().code(),
206             absl::StatusCode::kInvalidArgument);
207   EXPECT_THAT(redemption_request.status().message(),
208               HasSubstr("at least 32 bytes"));
209 }
210 
TEST_F(AnonymousTokensRedemptionClientTest,RepeatedTokenInRequest)211 TEST_F(AnonymousTokensRedemptionClientTest, RepeatedTokenInRequest) {
212   absl::StatusOr<AnonymousTokensRedemptionRequest> redemption_request =
213       client_->CreateAnonymousTokensRedemptionRequest(
214           {dummy_token_with_input_, dummy_token_with_input_});
215   EXPECT_EQ(redemption_request.status().code(),
216             absl::StatusCode::kInvalidArgument);
217   EXPECT_THAT(redemption_request.status().message(),
218               HasSubstr("should not be repeated"));
219 }
220 
TEST_F(AnonymousTokensRedemptionClientTest,ProcessBeforeRequestCreation)221 TEST_F(AnonymousTokensRedemptionClientTest, ProcessBeforeRequestCreation) {
222   AnonymousTokensRedemptionResponse redemption_resp;
223   absl::StatusOr<std::vector<RSABlindSignatureRedemptionResult>>
224       redemption_result =
225           client_->ProcessAnonymousTokensRedemptionResponse(redemption_resp);
226   EXPECT_EQ(redemption_result.status().code(),
227             absl::StatusCode::kFailedPrecondition);
228   EXPECT_THAT(redemption_result.status().message(),
229               HasSubstr("request was not created"));
230 }
231 
TEST_F(AnonymousTokensRedemptionClientTest,EmptyResponseProcessing)232 TEST_F(AnonymousTokensRedemptionClientTest, EmptyResponseProcessing) {
233   ANON_TOKENS_ASSERT_OK_AND_ASSIGN(
234       auto _, client_->CreateAnonymousTokensRedemptionRequest(
235                   {dummy_token_with_input_}));
236   AnonymousTokensRedemptionResponse redemption_resp;
237   absl::StatusOr<std::vector<RSABlindSignatureRedemptionResult>>
238       redemption_result =
239           client_->ProcessAnonymousTokensRedemptionResponse(redemption_resp);
240   EXPECT_EQ(redemption_result.status().code(),
241             absl::StatusCode::kInvalidArgument);
242   EXPECT_THAT(redemption_result.status().message(),
243               HasSubstr("empty response"));
244 }
245 
TEST_F(AnonymousTokensRedemptionClientTest,WrongSizeOfResponse)246 TEST_F(AnonymousTokensRedemptionClientTest, WrongSizeOfResponse) {
247   ANON_TOKENS_ASSERT_OK_AND_ASSIGN(
248       auto _, client_->CreateAnonymousTokensRedemptionRequest(
249                   {dummy_token_with_input_, GetRandomDummyTokenWithInput()}));
250   absl::StatusOr<std::vector<RSABlindSignatureRedemptionResult>>
251       redemption_result =
252           client_->ProcessAnonymousTokensRedemptionResponse(dummy_response_);
253   EXPECT_EQ(redemption_result.status().code(),
254             absl::StatusCode::kInvalidArgument);
255   EXPECT_THAT(redemption_result.status().message(),
256               HasSubstr("missing some requested token redemptions"));
257 }
258 
TEST_F(AnonymousTokensRedemptionClientTest,UseCaseMismatch)259 TEST_F(AnonymousTokensRedemptionClientTest, UseCaseMismatch) {
260   ANON_TOKENS_ASSERT_OK_AND_ASSIGN(
261       auto _, client_->CreateAnonymousTokensRedemptionRequest(
262                   {dummy_token_with_input_}));
263   dummy_response_.mutable_anonymous_token_redemption_results(0)->set_use_case(
264       AnonymousTokensUseCase_Name(TEST_USE_CASE_2));
265   absl::StatusOr<std::vector<RSABlindSignatureRedemptionResult>>
266       redemption_result =
267           client_->ProcessAnonymousTokensRedemptionResponse(dummy_response_);
268   EXPECT_EQ(redemption_result.status().code(),
269             absl::StatusCode::kInvalidArgument);
270   EXPECT_THAT(redemption_result.status().message(),
271               HasSubstr("Use case does not match"));
272 }
273 
TEST_F(AnonymousTokensRedemptionClientTest,KeyVersionMismatch)274 TEST_F(AnonymousTokensRedemptionClientTest, KeyVersionMismatch) {
275   ANON_TOKENS_ASSERT_OK_AND_ASSIGN(
276       auto _, client_->CreateAnonymousTokensRedemptionRequest(
277                   {dummy_token_with_input_}));
278   dummy_response_.mutable_anonymous_token_redemption_results(0)
279       ->set_key_version(2);
280   absl::StatusOr<std::vector<RSABlindSignatureRedemptionResult>>
281       redemption_result =
282           client_->ProcessAnonymousTokensRedemptionResponse(dummy_response_);
283   EXPECT_EQ(redemption_result.status().code(),
284             absl::StatusCode::kInvalidArgument);
285   EXPECT_THAT(redemption_result.status().message(),
286               HasSubstr("Key version does not match"));
287 }
288 
TEST_F(AnonymousTokensRedemptionClientTest,EmptyTokenInResponse)289 TEST_F(AnonymousTokensRedemptionClientTest, EmptyTokenInResponse) {
290   ANON_TOKENS_ASSERT_OK_AND_ASSIGN(
291       auto _, client_->CreateAnonymousTokensRedemptionRequest(
292                   {dummy_token_with_input_}));
293   dummy_response_.mutable_anonymous_token_redemption_results(0)
294       ->clear_serialized_unblinded_token();
295   absl::StatusOr<std::vector<RSABlindSignatureRedemptionResult>>
296       redemption_result =
297           client_->ProcessAnonymousTokensRedemptionResponse(dummy_response_);
298   EXPECT_EQ(redemption_result.status().code(),
299             absl::StatusCode::kInvalidArgument);
300   EXPECT_THAT(redemption_result.status().message(),
301               HasSubstr("Token cannot be empty"));
302 }
303 
TEST_F(AnonymousTokensRedemptionClientTest,MissingMaskInResponse)304 TEST_F(AnonymousTokensRedemptionClientTest, MissingMaskInResponse) {
305   ANON_TOKENS_ASSERT_OK_AND_ASSIGN(
306       auto _, client_->CreateAnonymousTokensRedemptionRequest(
307                   {dummy_token_with_input_}));
308   dummy_response_.mutable_anonymous_token_redemption_results(0)
309       ->clear_message_mask();
310   absl::StatusOr<std::vector<RSABlindSignatureRedemptionResult>>
311       redemption_result =
312           client_->ProcessAnonymousTokensRedemptionResponse(dummy_response_);
313   EXPECT_EQ(redemption_result.status().code(),
314             absl::StatusCode::kInvalidArgument);
315   EXPECT_THAT(
316       redemption_result.status().message(),
317       HasSubstr("Response message mask does not match input message mask"));
318 }
319 
TEST_F(AnonymousTokensRedemptionClientTest,WrongMaskSizeInResponse)320 TEST_F(AnonymousTokensRedemptionClientTest, WrongMaskSizeInResponse) {
321   ANON_TOKENS_ASSERT_OK_AND_ASSIGN(
322       auto _, client_->CreateAnonymousTokensRedemptionRequest(
323                   {dummy_token_with_input_}));
324   dummy_response_.mutable_anonymous_token_redemption_results(0)
325       ->set_message_mask(GetRandomString(31, &distr_u8_, &generator_));
326   absl::StatusOr<std::vector<RSABlindSignatureRedemptionResult>>
327       redemption_result =
328           client_->ProcessAnonymousTokensRedemptionResponse(dummy_response_);
329   EXPECT_EQ(redemption_result.status().code(),
330             absl::StatusCode::kInvalidArgument);
331   EXPECT_THAT(redemption_result.status().message(),
332               HasSubstr("at least 32 bytes"));
333 }
334 
TEST_F(AnonymousTokensRedemptionClientTest,RepeatedTokenInResponse)335 TEST_F(AnonymousTokensRedemptionClientTest, RepeatedTokenInResponse) {
336   auto another_dummy_token_with_input = GetRandomDummyTokenWithInput();
337   ANON_TOKENS_ASSERT_OK_AND_ASSIGN(
338       auto _, client_->CreateAnonymousTokensRedemptionRequest(
339                   {dummy_token_with_input_, another_dummy_token_with_input}));
340   another_dummy_token_with_input.mutable_token()->set_token(
341       dummy_token_with_input_.token().token());
342   *(dummy_response_.add_anonymous_token_redemption_results()) =
343       CreateRedemptionResultForTesting(another_dummy_token_with_input);
344   absl::StatusOr<std::vector<RSABlindSignatureRedemptionResult>>
345       redemption_result =
346           client_->ProcessAnonymousTokensRedemptionResponse(dummy_response_);
347   EXPECT_EQ(redemption_result.status().code(),
348             absl::StatusCode::kInvalidArgument);
349   EXPECT_THAT(redemption_result.status().message(),
350               HasSubstr("Token was repeated"));
351 }
352 
TEST_F(AnonymousTokensRedemptionClientTest,NewTokenInResponse)353 TEST_F(AnonymousTokensRedemptionClientTest, NewTokenInResponse) {
354   ANON_TOKENS_ASSERT_OK_AND_ASSIGN(
355       auto _, client_->CreateAnonymousTokensRedemptionRequest(
356                   {dummy_token_with_input_}));
357   dummy_response_.mutable_anonymous_token_redemption_results(0)
358       ->set_serialized_unblinded_token(
359           GetRandomString(512, &distr_u8_, &generator_));
360   absl::StatusOr<std::vector<RSABlindSignatureRedemptionResult>>
361       redemption_result =
362           client_->ProcessAnonymousTokensRedemptionResponse(dummy_response_);
363   EXPECT_EQ(redemption_result.status().code(),
364             absl::StatusCode::kInvalidArgument);
365   EXPECT_THAT(redemption_result.status().message(),
366               HasSubstr("tokens whose redemptions were not requested"));
367 }
368 
TEST_F(AnonymousTokensRedemptionClientTest,PublicMetadataMismatch)369 TEST_F(AnonymousTokensRedemptionClientTest, PublicMetadataMismatch) {
370   ANON_TOKENS_ASSERT_OK_AND_ASSIGN(
371       auto _, client_->CreateAnonymousTokensRedemptionRequest(
372                   {dummy_token_with_input_}));
373   dummy_response_.mutable_anonymous_token_redemption_results(0)
374       ->set_public_metadata(GetRandomString(10, &distr_u8_, &generator_));
375   absl::StatusOr<std::vector<RSABlindSignatureRedemptionResult>>
376       redemption_result =
377           client_->ProcessAnonymousTokensRedemptionResponse(dummy_response_);
378   EXPECT_EQ(redemption_result.status().code(),
379             absl::StatusCode::kInvalidArgument);
380   EXPECT_THAT(redemption_result.status().message(),
381               HasSubstr("Response metadata does not match"));
382 }
383 
TEST_F(AnonymousTokensRedemptionClientTest,PlaintextMessageMismatch)384 TEST_F(AnonymousTokensRedemptionClientTest, PlaintextMessageMismatch) {
385   ANON_TOKENS_ASSERT_OK_AND_ASSIGN(
386       auto _, client_->CreateAnonymousTokensRedemptionRequest(
387                   {dummy_token_with_input_}));
388   dummy_response_.mutable_anonymous_token_redemption_results(0)
389       ->set_plaintext_message(GetRandomString(20, &distr_u8_, &generator_));
390   absl::StatusOr<std::vector<RSABlindSignatureRedemptionResult>>
391       redemption_result =
392           client_->ProcessAnonymousTokensRedemptionResponse(dummy_response_);
393   EXPECT_EQ(redemption_result.status().code(),
394             absl::StatusCode::kInvalidArgument);
395   EXPECT_THAT(redemption_result.status().message(),
396               HasSubstr("Response plaintext message does not match"));
397 }
398 
TEST_F(AnonymousTokensRedemptionClientTest,MessageMaskMismatch)399 TEST_F(AnonymousTokensRedemptionClientTest, MessageMaskMismatch) {
400   ANON_TOKENS_ASSERT_OK_AND_ASSIGN(
401       auto _, client_->CreateAnonymousTokensRedemptionRequest(
402                   {dummy_token_with_input_}));
403   dummy_response_.mutable_anonymous_token_redemption_results(0)
404       ->set_message_mask(GetRandomString(32, &distr_u8_, &generator_));
405   absl::StatusOr<std::vector<RSABlindSignatureRedemptionResult>>
406       redemption_result =
407           client_->ProcessAnonymousTokensRedemptionResponse(dummy_response_);
408   EXPECT_EQ(redemption_result.status().code(),
409             absl::StatusCode::kInvalidArgument);
410   EXPECT_THAT(redemption_result.status().message(),
411               HasSubstr("Response message mask does not match"));
412 }
413 
TEST_F(AnonymousTokensRedemptionClientTest,SuccessfulResponseProcessingWithOneToken)414 TEST_F(AnonymousTokensRedemptionClientTest,
415        SuccessfulResponseProcessingWithOneToken) {
416   // Only one token in request
417   ANON_TOKENS_ASSERT_OK_AND_ASSIGN(
418       auto redemption_request, client_->CreateAnonymousTokensRedemptionRequest(
419                                    {dummy_token_with_input_}));
420   ANON_TOKENS_ASSERT_OK_AND_ASSIGN(
421       auto rsa_blind_sig_redemption_results,
422       client_->ProcessAnonymousTokensRedemptionResponse(dummy_response_));
423   auto tokens_to_result_map = CreateTokenToRedemptionResultMap(dummy_response_);
424   // Checks
425   ASSERT_EQ(rsa_blind_sig_redemption_results.size(), 1);
426   std::string token =
427       rsa_blind_sig_redemption_results[0].token_with_input().token().token();
428   ASSERT_TRUE(tokens_to_result_map.contains(token));
429   EXPECT_EQ(rsa_blind_sig_redemption_results[0]
430                 .token_with_input()
431                 .input()
432                 .plaintext_message(),
433             tokens_to_result_map[token].plaintext_message);
434   EXPECT_TRUE(!rsa_blind_sig_redemption_results[0]
435                    .token_with_input()
436                    .token()
437                    .message_mask()
438                    .empty());
439   EXPECT_EQ(rsa_blind_sig_redemption_results[0]
440                 .token_with_input()
441                 .input()
442                 .public_metadata(),
443             tokens_to_result_map[token].public_metadata);
444   EXPECT_EQ(rsa_blind_sig_redemption_results[0].redeemed(),
445             tokens_to_result_map[token].redeemed);
446   EXPECT_EQ(rsa_blind_sig_redemption_results[0].double_spent(),
447             tokens_to_result_map[token].double_spent);
448 }
449 
TEST_F(AnonymousTokensRedemptionClientTest,SuccessfulResponseProcessingWithMultipleToken)450 TEST_F(AnonymousTokensRedemptionClientTest,
451        SuccessfulResponseProcessingWithMultipleToken) {
452   RSABlindSignatureTokenWithInput token_with_empty_message =
453       GetRandomDummyTokenWithInput();
454   token_with_empty_message.mutable_input()->clear_plaintext_message();
455   RSABlindSignatureTokenWithInput token_with_empty_mask =
456       GetRandomDummyTokenWithInput();
457   token_with_empty_mask.mutable_token()->clear_message_mask();
458   std::vector<RSABlindSignatureTokenWithInput> tokens_with_inputs = {
459       dummy_token_with_input_, GetRandomDummyTokenWithInput(),
460       GetRandomDummyTokenWithInput(), token_with_empty_message,
461       token_with_empty_mask};
462   ANON_TOKENS_ASSERT_OK_AND_ASSIGN(
463       auto _,
464       client_->CreateAnonymousTokensRedemptionRequest(tokens_with_inputs));
465   *(dummy_response_.add_anonymous_token_redemption_results()) =
466       CreateRedemptionResultForTesting(tokens_with_inputs[1], false, true,
467                                        TEST_USE_CASE, 1);
468   for (int i = 2; i < tokens_with_inputs.size(); ++i) {
469     *(dummy_response_.add_anonymous_token_redemption_results()) =
470         CreateRedemptionResultForTesting(tokens_with_inputs[i]);
471   }
472   ANON_TOKENS_ASSERT_OK_AND_ASSIGN(
473       auto rsa_blind_sig_redemption_results,
474       client_->ProcessAnonymousTokensRedemptionResponse(dummy_response_));
475   auto tokens_to_result_map = CreateTokenToRedemptionResultMap(dummy_response_);
476   // Checks
477   ASSERT_EQ(tokens_with_inputs.size(), rsa_blind_sig_redemption_results.size());
478   for (int i = 0; i < rsa_blind_sig_redemption_results.size(); ++i) {
479     std::string token =
480         rsa_blind_sig_redemption_results[i].token_with_input().token().token();
481     ASSERT_TRUE(tokens_to_result_map.contains(token));
482     EXPECT_EQ(rsa_blind_sig_redemption_results[i]
483                   .token_with_input()
484                   .input()
485                   .plaintext_message(),
486               tokens_to_result_map[token].plaintext_message);
487     EXPECT_EQ(rsa_blind_sig_redemption_results[i]
488                   .token_with_input()
489                   .token()
490                   .message_mask(),
491               tokens_to_result_map[token].message_mask);
492     EXPECT_EQ(rsa_blind_sig_redemption_results[i]
493                   .token_with_input()
494                   .input()
495                   .public_metadata(),
496               tokens_to_result_map[token].public_metadata);
497     EXPECT_EQ(rsa_blind_sig_redemption_results[i].redeemed(),
498               tokens_to_result_map[token].redeemed);
499     EXPECT_EQ(rsa_blind_sig_redemption_results[i].double_spent(),
500               tokens_to_result_map[token].double_spent);
501   }
502 }
503 
504 }  // namespace
505 }  // namespace anonymous_tokens
506