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_rsa_bssa_client.h"
16
17 #include <cstddef>
18 #include <memory>
19 #include <optional>
20 #include <string>
21 #include <utility>
22 #include <vector>
23
24 #include "absl/container/flat_hash_set.h"
25 #include "absl/status/status.h"
26 #include "absl/time/time.h"
27 #include "anonymous_tokens/cpp/crypto/anonymous_tokens_pb_openssl_converters.h"
28 #include "anonymous_tokens/cpp/crypto/crypto_utils.h"
29 #include "anonymous_tokens/cpp/shared/proto_utils.h"
30 #include "anonymous_tokens/cpp/shared/status_utils.h"
31 #include "anonymous_tokens/proto/anonymous_tokens.pb.h"
32
33 namespace anonymous_tokens {
34
35 namespace {
36
ValidityChecksForClientCreation(const RSABlindSignaturePublicKey & public_key)37 absl::Status ValidityChecksForClientCreation(
38 const RSABlindSignaturePublicKey& public_key) {
39 // Basic validity checks.
40 if (!ParseUseCase(public_key.use_case()).ok()) {
41 return absl::InvalidArgumentError("Invalid use case for public key.");
42 } else if (public_key.key_version() <= 0) {
43 return absl::InvalidArgumentError(
44 "Key version cannot be zero or negative.");
45 } else if (public_key.key_size() < 256) {
46 return absl::InvalidArgumentError(
47 "Key modulus size cannot be less than 256 bytes.");
48 } else if (public_key.mask_gen_function() == AT_TEST_MGF ||
49 public_key.mask_gen_function() == AT_MGF_UNDEFINED) {
50 return absl::InvalidArgumentError("Unknown or unacceptable mgf1 hash.");
51 } else if (public_key.sig_hash_type() == AT_TEST_HASH_TYPE ||
52 public_key.sig_hash_type() == AT_HASH_TYPE_UNDEFINED) {
53 return absl::InvalidArgumentError(
54 "Unknown or unacceptable signature hash.");
55 } else if (public_key.salt_length() <= 0) {
56 return absl::InvalidArgumentError(
57 "Non-positive salt length is not allowed.");
58 }
59
60 switch (public_key.message_mask_type()) {
61 case AT_MESSAGE_MASK_CONCAT:
62 if (public_key.message_mask_size() < 32) {
63 return absl::InvalidArgumentError(
64 "Message mask concat type must have a size of at least 32 bytes.");
65 }
66 break;
67 case AT_MESSAGE_MASK_NO_MASK:
68 if (public_key.message_mask_size() != 0) {
69 return absl::InvalidArgumentError(
70 "Message mask no mask type must be set to size 0 bytes.");
71 }
72 break;
73 default:
74 return absl::InvalidArgumentError(
75 "Message mask type must be defined and supported.");
76 }
77
78 RSAPublicKey rsa_public_key;
79 if (!rsa_public_key.ParseFromString(public_key.serialized_public_key())) {
80 return absl::InvalidArgumentError("Public key is malformed.");
81 }
82 if (rsa_public_key.n().size() != static_cast<size_t>(public_key.key_size())) {
83 return absl::InvalidArgumentError(
84 "Public key size does not match key size.");
85 }
86 return absl::OkStatus();
87 }
88
89 } // namespace
90
AnonymousTokensRsaBssaClient(const RSABlindSignaturePublicKey & public_key)91 AnonymousTokensRsaBssaClient::AnonymousTokensRsaBssaClient(
92 const RSABlindSignaturePublicKey& public_key)
93 : public_key_(public_key) {}
94
95 absl::StatusOr<std::unique_ptr<AnonymousTokensRsaBssaClient>>
Create(const RSABlindSignaturePublicKey & public_key)96 AnonymousTokensRsaBssaClient::Create(
97 const RSABlindSignaturePublicKey& public_key) {
98 ANON_TOKENS_RETURN_IF_ERROR(ValidityChecksForClientCreation(public_key));
99 return absl::WrapUnique(new AnonymousTokensRsaBssaClient(public_key));
100 }
101
102 absl::StatusOr<AnonymousTokensSignRequest>
CreateRequest(const std::vector<PlaintextMessageWithPublicMetadata> & inputs)103 AnonymousTokensRsaBssaClient::CreateRequest(
104 const std::vector<PlaintextMessageWithPublicMetadata>& inputs) {
105 if (inputs.empty()) {
106 return absl::InvalidArgumentError("Cannot create an empty request.");
107 } else if (!blinding_info_map_.empty()) {
108 return absl::FailedPreconditionError(
109 "Blind signature request already created.");
110 }
111
112 RSAPublicKey rsa_public_key_proto;
113 if (!rsa_public_key_proto.ParseFromString(
114 public_key_.serialized_public_key())) {
115 return absl::InvalidArgumentError("Public key is malformed.");
116 }
117
118 AnonymousTokensSignRequest request;
119 for (const PlaintextMessageWithPublicMetadata& input : inputs) {
120 // Generate nonce and masked message. For more details, see
121 // https://datatracker.ietf.org/doc/draft-irtf-cfrg-rsa-blind-signatures/
122 ANON_TOKENS_ASSIGN_OR_RETURN(std::string mask, GenerateMask(public_key_));
123 std::string masked_message =
124 MaskMessageConcat(mask, input.plaintext_message());
125
126 std::optional<std::string> public_metadata = std::nullopt;
127 if (public_key_.public_metadata_support()) {
128 // Empty public metadata is a valid value.
129 public_metadata = input.public_metadata();
130 }
131 const bool use_rsa_public_exponent = false;
132 // Owned by BoringSSL.
133 ANON_TOKENS_ASSIGN_OR_RETURN(
134 const EVP_MD* sig_hash,
135 ProtoHashTypeToEVPDigest(public_key_.sig_hash_type()));
136 // Owned by BoringSSL.
137 ANON_TOKENS_ASSIGN_OR_RETURN(
138 const EVP_MD* mgf1_hash,
139 ProtoMaskGenFunctionToEVPDigest(public_key_.mask_gen_function()));
140 // Generate RSA blinder.
141 ANON_TOKENS_ASSIGN_OR_RETURN(
142 auto rsa_bssa_blinder,
143 RsaBlinder::New(rsa_public_key_proto.n(), rsa_public_key_proto.e(),
144 sig_hash, mgf1_hash, public_key_.salt_length(),
145 use_rsa_public_exponent, public_metadata));
146 ANON_TOKENS_ASSIGN_OR_RETURN(const std::string blinded_message,
147 rsa_bssa_blinder->Blind(masked_message));
148
149 // Store randomness needed to unblind.
150 BlindingInfo blinding_info = {
151 input,
152 mask,
153 std::move(rsa_bssa_blinder),
154 };
155
156 // Create the blinded token.
157 AnonymousTokensSignRequest_BlindedToken* blinded_token =
158 request.add_blinded_tokens();
159 blinded_token->set_use_case(public_key_.use_case());
160 blinded_token->set_key_version(public_key_.key_version());
161 blinded_token->set_serialized_token(blinded_message);
162 blinded_token->set_public_metadata(input.public_metadata());
163 blinded_token->set_do_not_use_rsa_public_exponent(!use_rsa_public_exponent);
164 blinding_info_map_[blinded_message] = std::move(blinding_info);
165 }
166
167 return request;
168 }
169
170 absl::StatusOr<std::vector<RSABlindSignatureTokenWithInput>>
ProcessResponse(const AnonymousTokensSignResponse & response)171 AnonymousTokensRsaBssaClient::ProcessResponse(
172 const AnonymousTokensSignResponse& response) {
173 if (blinding_info_map_.empty()) {
174 return absl::FailedPreconditionError(
175 "A valid Blind signature request was not created before calling "
176 "RetrieveAnonymousTokensFromSignResponse.");
177 } else if (response.anonymous_tokens().empty()) {
178 return absl::InvalidArgumentError("Cannot process an empty response.");
179 } else if (static_cast<size_t>(response.anonymous_tokens().size()) !=
180 blinding_info_map_.size()) {
181 return absl::InvalidArgumentError(
182 "Response is missing some requested tokens.");
183 }
184
185 // Vector to accumulate output tokens.
186 std::vector<RSABlindSignatureTokenWithInput> tokens;
187
188 // Temporary set structure to check for duplicate responses.
189 absl::flat_hash_set<absl::string_view> blinded_messages;
190
191 // Loop over all the anonymous tokens in the response.
192 for (const AnonymousTokensSignResponse_AnonymousToken& anonymous_token :
193 response.anonymous_tokens()) {
194 // Basic validity checks on the response.
195 if (anonymous_token.use_case() != public_key_.use_case()) {
196 return absl::InvalidArgumentError("Use case does not match public key.");
197 } else if (anonymous_token.key_version() != public_key_.key_version()) {
198 return absl::InvalidArgumentError(
199 "Key version does not match public key.");
200 } else if (anonymous_token.serialized_blinded_message().empty()) {
201 return absl::InvalidArgumentError(
202 "Blinded message that was sent in request cannot be empty in "
203 "response.");
204 } else if (anonymous_token.serialized_token().empty()) {
205 return absl::InvalidArgumentError(
206 "Blinded anonymous token (serialized_token) in response cannot be "
207 "empty.");
208 }
209
210 // Check for duplicate in responses.
211 if (!blinded_messages.insert(anonymous_token.serialized_blinded_message())
212 .second) {
213 return absl::InvalidArgumentError(
214 "Blinded message was repeated in the response.");
215 }
216
217 // Retrieve blinding info associated with blind response.
218 auto it =
219 blinding_info_map_.find(anonymous_token.serialized_blinded_message());
220 if (it == blinding_info_map_.end()) {
221 return absl::InvalidArgumentError(
222 "Response has some tokens for some blinded messages that were not "
223 "requested.");
224 }
225 const BlindingInfo& blinding_info = it->second;
226
227 if (blinding_info.input.public_metadata() !=
228 anonymous_token.public_metadata()) {
229 return absl::InvalidArgumentError(
230 "Response public metadata does not match input.");
231 } else if (public_key_.public_metadata_support() &&
232 !anonymous_token.do_not_use_rsa_public_exponent()) {
233 // Bool do_not_use_rsa_public_exponent does not matter for the non-public
234 // metadata version.
235 return absl::InvalidArgumentError(
236 "Setting do_not_use_rsa_public_exponent to false is no longer "
237 "supported.");
238 }
239
240 // Unblind the blinded anonymous token to obtain the final anonymous token
241 // (signature).
242 ANON_TOKENS_ASSIGN_OR_RETURN(
243 const std::string final_anonymous_token,
244 blinding_info.rsa_blinder->Unblind(anonymous_token.serialized_token()));
245
246 // Verify the signature for correctness.
247 ANON_TOKENS_RETURN_IF_ERROR(blinding_info.rsa_blinder->Verify(
248 final_anonymous_token,
249 MaskMessageConcat(blinding_info.mask,
250 blinding_info.input.plaintext_message())));
251
252 // Construct the final signature proto.
253 RSABlindSignatureTokenWithInput final_token_proto;
254 *final_token_proto.mutable_token()->mutable_token() = final_anonymous_token;
255 *final_token_proto.mutable_token()->mutable_message_mask() =
256 blinding_info.mask;
257 *final_token_proto.mutable_input() = blinding_info.input;
258
259 tokens.push_back(final_token_proto);
260 }
261
262 return tokens;
263 }
264
Verify(const RSABlindSignaturePublicKey &,const RSABlindSignatureToken &,const PlaintextMessageWithPublicMetadata &)265 absl::Status AnonymousTokensRsaBssaClient::Verify(
266 const RSABlindSignaturePublicKey& /*public_key*/,
267 const RSABlindSignatureToken& /*token*/,
268 const PlaintextMessageWithPublicMetadata& /*input*/) {
269 return absl::UnimplementedError("Verify not implemented yet.");
270 }
271
272 } // namespace anonymous_tokens
273