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