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