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/privacy_pass/token_encodings.h"
16
17 #include <sys/types.h>
18
19 #include <cstddef>
20 #include <cstdint>
21 #include <optional>
22 #include <string>
23
24 #include "absl/status/status.h"
25 #include "absl/status/statusor.h"
26 #include "absl/strings/ascii.h"
27 #include "absl/strings/str_cat.h"
28 #include "absl/strings/str_format.h"
29 #include "absl/strings/str_split.h"
30 #include "absl/time/time.h"
31 #include "absl/types/span.h"
32 #include "anonymous_tokens/cpp/shared/status_utils.h"
33 #include <openssl/base.h>
34 #include <openssl/bytestring.h>
35 #include <openssl/mem.h>
36
37 namespace anonymous_tokens {
38
39 namespace {
40
EncodeTokenStructHelper(const uint16_t & token_type,const std::string & token_key_id,const std::string & nonce,const std::string & context,const std::optional<std::string> authenticator)41 absl::StatusOr<std::string> EncodeTokenStructHelper(
42 const uint16_t& token_type, const std::string& token_key_id,
43 const std::string& nonce, const std::string& context,
44 const std::optional<std::string> authenticator) {
45 // Main CryptoByteBuilder object cbb which will be passed to CBB_finish to
46 // finalize the output string.
47 bssl::ScopedCBB cbb;
48 // initial_capacity only serves as a hint.
49 if (!CBB_init(cbb.get(), /*initial_capacity=*/98)) {
50 return absl::InternalError("CBB_init() failed.");
51 }
52 // Add token_type to cbb.
53 if (!CBB_add_u16(cbb.get(), token_type) ||
54 // Add nonce to cbb.
55 !CBB_add_bytes(cbb.get(), reinterpret_cast<const uint8_t*>(nonce.data()),
56 nonce.size()) ||
57 // Add context string to cbb.
58 !CBB_add_bytes(cbb.get(),
59 reinterpret_cast<const uint8_t*>(context.data()),
60 context.size()) ||
61 // Add token_key_id to cbb.
62 !CBB_add_bytes(cbb.get(),
63 reinterpret_cast<const uint8_t*>(token_key_id.data()),
64 token_key_id.size())) {
65 return absl::InvalidArgumentError(
66 "Could not construct cbb with given inputs.");
67 }
68 // Add authenticator to cbb.
69 if (authenticator.has_value() &&
70 !CBB_add_bytes(cbb.get(),
71 reinterpret_cast<const uint8_t*>(authenticator->data()),
72 authenticator->size())) {
73 return absl::InvalidArgumentError("Could not add authenticator to cbb.");
74 }
75 uint8_t* encoded_output;
76 size_t encoded_output_len;
77 if (!CBB_finish(cbb.get(), &encoded_output, &encoded_output_len)) {
78 return absl::InvalidArgumentError(
79 "Failed to generate token / token input encoding");
80 }
81 std::string encoded_output_str(reinterpret_cast<const char*>(encoded_output),
82 encoded_output_len);
83 // Free memory.
84 OPENSSL_free(encoded_output);
85 return encoded_output_str;
86 }
87
88 // `extensions_cbs` may contain one or more encoded extensions in a row.
89 // This function only decodes the first extension from `extensions_cbs`. After
90 // this function is called `extensions_cbs` will point to the next extension
91 // after the one returned by decodeExtensions. If an error is returned,
92 // `extensions_cbs` may be in a partially read state - do not rely on it to
93 // parse more extensions.
decodeExtension(CBS * extensions_cbs)94 absl::StatusOr<Extension> decodeExtension(CBS* extensions_cbs) {
95 Extension ext;
96 if (!CBS_get_u16(extensions_cbs, &ext.extension_type)) {
97 return absl::InvalidArgumentError("failed to read next type.");
98 }
99 CBS extension_cbs;
100 if (!CBS_get_u16_length_prefixed(extensions_cbs, &extension_cbs)) {
101 return absl::InvalidArgumentError("failed to read extension value.");
102 }
103 ext.extension_value.resize(CBS_len(&extension_cbs));
104 if (!CBS_copy_bytes(&extension_cbs,
105 reinterpret_cast<uint8_t*>(ext.extension_value.data()),
106 CBS_len(&extension_cbs))) {
107 return absl::InvalidArgumentError("failed to read Extension value.");
108 }
109 return ext;
110 }
111
112 } // namespace
113
AuthenticatorInput(const Token & token)114 absl::StatusOr<std::string> AuthenticatorInput(const Token& token) {
115 return EncodeTokenStructHelper(token.token_type, token.token_key_id,
116 token.nonce, token.context, std::nullopt);
117 }
118
MarshalToken(const Token & token)119 absl::StatusOr<std::string> MarshalToken(const Token& token) {
120 return EncodeTokenStructHelper(token.token_type, token.token_key_id,
121 token.nonce, token.context,
122 token.authenticator);
123 }
124
UnmarshalToken(std::string token)125 absl::StatusOr<Token> UnmarshalToken(std::string token) {
126 Token out;
127 out.nonce.resize(32);
128 out.context.resize(32);
129 out.token_key_id.resize(32);
130 out.authenticator.resize(256);
131 CBS cbs;
132 CBS_init(&cbs, reinterpret_cast<const uint8_t*>(token.data()), token.size());
133 if (!CBS_get_u16(&cbs, &out.token_type)) {
134 return absl::InvalidArgumentError("failed to read token type");
135 }
136 if (out.token_type != 0xDA7A) {
137 return absl::InvalidArgumentError("unsupported token type");
138 }
139 if (!CBS_copy_bytes(&cbs, reinterpret_cast<uint8_t*>(out.nonce.data()),
140 out.nonce.size())) {
141 return absl::InvalidArgumentError("failed to read nonce");
142 }
143 if (!CBS_copy_bytes(&cbs, reinterpret_cast<uint8_t*>(out.context.data()),
144 out.context.size())) {
145 return absl::InvalidArgumentError("failed to read context");
146 }
147 if (!CBS_copy_bytes(&cbs, reinterpret_cast<uint8_t*>(out.token_key_id.data()),
148 out.token_key_id.size())) {
149 return absl::InvalidArgumentError("failed to read token_key_id");
150 }
151 if (!CBS_copy_bytes(&cbs,
152 reinterpret_cast<uint8_t*>(out.authenticator.data()),
153 out.authenticator.size())) {
154 return absl::InvalidArgumentError("failed to read authenticator");
155 }
156 if (CBS_len(&cbs) != 0) {
157 return absl::InvalidArgumentError("token had extra bytes");
158 }
159 return out;
160 }
161
EncodeExtension(const Extension & extension)162 absl::StatusOr<std::string> EncodeExtension(const Extension& extension) {
163 // Main CryptoByteBuilder object cbb which will be passed to CBB_finish to
164 // finalize the output string.
165 bssl::ScopedCBB cbb;
166 // Temporary cbb struct object to fill with value bytes.
167 CBB extension_value;
168 // initial_capacity only serves as a hint.
169 if (!CBB_init(cbb.get(), /*initial_capacity=*/4)) {
170 return absl::InternalError("CBB_init() failed.");
171 }
172 // Add extension type using only 2 bytes.
173 if (!CBB_add_u16(cbb.get(), extension.extension_type) ||
174 // Add extension value but prefix it with its length which should fit in 2
175 // bytes.
176 !CBB_add_u16_length_prefixed(cbb.get(), &extension_value) ||
177 !CBB_add_bytes(
178 &extension_value,
179 reinterpret_cast<const uint8_t*>(extension.extension_value.data()),
180 extension.extension_value.size())) {
181 return absl::InvalidArgumentError("Failed to populate cbb.");
182 }
183
184 uint8_t* encoded_ext;
185 size_t encoded_ext_len;
186 if (!CBB_finish(cbb.get(), &encoded_ext, &encoded_ext_len)) {
187 return absl::InvalidArgumentError("Failed to generate extension encoding");
188 }
189 std::string encoded_ext_str(reinterpret_cast<const char*>(encoded_ext),
190 encoded_ext_len);
191 // Free memory.
192 OPENSSL_free(encoded_ext);
193 return encoded_ext_str;
194 }
195
EncodeExtensions(const Extensions & extensions)196 absl::StatusOr<std::string> EncodeExtensions(const Extensions& extensions) {
197 // Main CryptoByteBuilder object cbb which will be passed to CBB_finish to
198 // finalize the output string.
199 bssl::ScopedCBB cbb;
200 // Temporary cbb struct object to fill with encoded extensions bytes.
201 CBB extensions_list;
202 // initial_capacity only serves as a hint.
203 if (!CBB_init(cbb.get(), /*initial_capacity=*/6)) {
204 return absl::InternalError("CBB_init() failed.");
205 }
206 // Size in bytes for the extensions list must fit in 2 bytes.
207 if (!CBB_add_u16_length_prefixed(cbb.get(), &extensions_list)) {
208 return absl::InvalidArgumentError(
209 "Call to CBB_add_u16_length_prefixed erred.");
210 }
211 // Add all encoded extensions to the temporary cbb.
212 for (const Extension& ext : extensions.extensions) {
213 ANON_TOKENS_ASSIGN_OR_RETURN(std::string encoded_ext, EncodeExtension(ext));
214 if (!CBB_add_bytes(&extensions_list,
215 reinterpret_cast<const uint8_t*>(encoded_ext.data()),
216 encoded_ext.size())) {
217 return absl::InvalidArgumentError(
218 "Could not add encoded extension to cbb.");
219 }
220 }
221
222 uint8_t* encoded_extensions;
223 size_t encoded_extensions_len;
224 if (!CBB_finish(cbb.get(), &encoded_extensions, &encoded_extensions_len)) {
225 return absl::InvalidArgumentError(
226 "Failed to generate encoded extensions list.");
227 }
228 std::string encoded_extensions_str(
229 reinterpret_cast<const char*>(encoded_extensions),
230 encoded_extensions_len);
231 // Free memory.
232 OPENSSL_free(encoded_extensions);
233 return encoded_extensions_str;
234 }
235
FromExtension(const Extension & ext)236 absl::StatusOr<ExpirationTimestamp> ExpirationTimestamp::FromExtension(
237 const Extension& ext) {
238 if (ext.extension_type != 0x0001) {
239 return absl::InvalidArgumentError(
240 absl::StrCat("Extension of wrong type: ", ext.extension_type));
241 }
242 ExpirationTimestamp ts;
243 CBS cbs;
244 CBS_init(&cbs, reinterpret_cast<const uint8_t*>(ext.extension_value.data()),
245 ext.extension_value.size());
246 if (!CBS_get_u64(&cbs, &ts.timestamp_precision)) {
247 return absl::InvalidArgumentError("failed to read timestamp_precision");
248 }
249 if (!CBS_get_u64(&cbs, &ts.timestamp)) {
250 return absl::InvalidArgumentError("failed to read timestamp");
251 }
252 return ts;
253 }
254
AsExtension() const255 absl::StatusOr<Extension> ExpirationTimestamp::AsExtension() const {
256 bssl::ScopedCBB cbb;
257 if (!CBB_init(cbb.get(), sizeof(ExpirationTimestamp))) {
258 return absl::InternalError("CBB_init() failed.");
259 }
260 if (!CBB_add_u64(cbb.get(), timestamp_precision)) {
261 return absl::InternalError("Failed to add timestamp_precision to cbb.");
262 }
263 if (!CBB_add_u64(cbb.get(), timestamp)) {
264 return absl::InternalError("Failed to add timestamp to cbb.");
265 }
266 uint8_t* wire_format;
267 size_t wire_format_len;
268 if (!CBB_finish(cbb.get(), &wire_format, &wire_format_len)) {
269 return absl::InternalError("Failed to generate wire format.");
270 }
271 std::string wire_format_str(reinterpret_cast<const char*>(wire_format),
272 wire_format_len);
273 OPENSSL_free(wire_format);
274 return Extension{
275 .extension_type = 0x0001,
276 .extension_value = wire_format_str,
277 };
278 }
279
AsExtension() const280 absl::StatusOr<Extension> GeoHint::AsExtension() const {
281 bssl::ScopedCBB cbb;
282 if (!CBB_init(cbb.get(), sizeof(uint16_t) + geo_hint.size())) {
283 return absl::InternalError("CBB_init() failed.");
284 }
285 // Temporary cbb struct object to fill with geo_hint bytes.
286 CBB geo_hint_cbb;
287 if (!CBB_add_u16_length_prefixed(cbb.get(), &geo_hint_cbb) ||
288 !CBB_add_bytes(&geo_hint_cbb,
289 reinterpret_cast<const uint8_t*>(geo_hint.data()),
290 geo_hint.size())) {
291 return absl::InternalError("Failed to add geohint to cbb.");
292 }
293 uint8_t* wire_format;
294 size_t wire_format_len;
295 if (!CBB_finish(cbb.get(), &wire_format, &wire_format_len)) {
296 return absl::InternalError("Failed to generate wire format.");
297 }
298 std::string wire_format_str(reinterpret_cast<const char*>(wire_format),
299 wire_format_len);
300 OPENSSL_free(wire_format);
301 return Extension{
302 .extension_type = 0x0002,
303 .extension_value = wire_format_str,
304 };
305 }
306
FromExtension(const Extension & ext)307 absl::StatusOr<GeoHint> GeoHint::FromExtension(const Extension& ext) {
308 if (ext.extension_type != 0x0002) {
309 return absl::InvalidArgumentError(absl::StrCat(
310 "[GeoHint] Extension of wrong type: ", ext.extension_type));
311 }
312 GeoHint gh;
313 CBS cbs;
314 CBS_init(&cbs, reinterpret_cast<const uint8_t*>(ext.extension_value.data()),
315 ext.extension_value.size());
316 CBS geohint_cbs;
317 if (!CBS_get_u16_length_prefixed(&cbs, &geohint_cbs)) {
318 return absl::InvalidArgumentError(
319 "[GeoHint] failed to read geohint length");
320 }
321 gh.geo_hint.resize(CBS_len(&geohint_cbs));
322 if (!CBS_copy_bytes(&geohint_cbs,
323 reinterpret_cast<uint8_t*>(gh.geo_hint.data()),
324 gh.geo_hint.size())) {
325 return absl::InvalidArgumentError("[GeoHint] failed to read geohint data");
326 }
327
328 const std::vector<std::string_view> split = absl::StrSplit(gh.geo_hint, ',');
329 if (split.size() != 3) {
330 return absl::InvalidArgumentError(
331 "[GeoHint] geo_hint must be exactly 3 parts.");
332 }
333 for (const std::string_view part : split) {
334 if (absl::AsciiStrToUpper(part) != part) {
335 return absl::InvalidArgumentError(
336 "[GeoHint] all geo_hint parts must be UPPERCASE.");
337 }
338 }
339 gh.country_code = split[0];
340 gh.region = split[1];
341 gh.city = split[2];
342 return gh;
343 }
344
AsExtension() const345 absl::StatusOr<Extension> ServiceType::AsExtension() const {
346 bssl::ScopedCBB cbb;
347 if (!CBB_init(cbb.get(), sizeof(uint8_t))) {
348 return absl::InternalError("CBB_init() failed.");
349 }
350 if (!CBB_add_u8(cbb.get(), service_type_id)) {
351 return absl::InternalError("Failed to add service_type_id to cbb.");
352 }
353 uint8_t* wire_format;
354 size_t wire_format_len;
355 if (!CBB_finish(cbb.get(), &wire_format, &wire_format_len)) {
356 return absl::InternalError("Failed to generate wire format.");
357 }
358 std::string wire_format_str(reinterpret_cast<const char*>(wire_format),
359 wire_format_len);
360 OPENSSL_free(wire_format);
361 return Extension{.extension_type = 0xF001,
362 .extension_value = wire_format_str};
363 }
364
FromExtension(const Extension & ext)365 absl::StatusOr<ServiceType> ServiceType::FromExtension(const Extension& ext) {
366 if (ext.extension_type != 0xF001) {
367 return absl::InvalidArgumentError(absl::StrCat(
368 "[ServiceType] extension of wrong type: ", ext.extension_type));
369 }
370 ServiceType st;
371 CBS cbs;
372 CBS_init(&cbs, reinterpret_cast<const uint8_t*>(ext.extension_value.data()),
373 ext.extension_value.size());
374 if (!CBS_get_u8(&cbs, &st.service_type_id)) {
375 return absl::InvalidArgumentError(
376 "[ServiceType] failed to read len from extension");
377 }
378 switch (st.service_type_id) {
379 case kChromeIpBlinding:
380 st.service_type = "chromeipblinding";
381 break;
382 default:
383 return absl::InvalidArgumentError(
384 "[ServiceType] unknown service_type_id");
385 }
386 return st;
387 }
388
AsExtension() const389 absl::StatusOr<Extension> DebugMode::AsExtension() const {
390 bssl::ScopedCBB cbb;
391 if (!CBB_init(cbb.get(), sizeof(uint8_t))) {
392 return absl::InternalError("CBB_init() failed.");
393 }
394 if (!CBB_add_u8(cbb.get(), mode)) {
395 return absl::InternalError("Failed to add mode to cbb.");
396 }
397 uint8_t* wire_format;
398 size_t wire_format_len;
399 if (!CBB_finish(cbb.get(), &wire_format, &wire_format_len)) {
400 return absl::InternalError("Failed to generate wire format.");
401 }
402 std::string wire_format_str(reinterpret_cast<const char*>(wire_format),
403 wire_format_len);
404 OPENSSL_free(wire_format);
405 return Extension{.extension_type = 0xF002,
406 .extension_value = wire_format_str};
407 }
408
FromExtension(const Extension & ext)409 absl::StatusOr<DebugMode> DebugMode::FromExtension(const Extension& ext) {
410 if (ext.extension_type != 0xF002) {
411 return absl::InvalidArgumentError(absl::StrCat(
412 "[DebugMode] extension of wrong type: ", ext.extension_type));
413 }
414 DebugMode dm;
415 CBS cbs;
416 CBS_init(&cbs, reinterpret_cast<const uint8_t*>(ext.extension_value.data()),
417 ext.extension_value.size());
418 if (!CBS_get_u8(&cbs, &dm.mode)) {
419 return absl::InvalidArgumentError(
420 "[DebugMode] failed to read len from extension");
421 }
422 if (dm.mode != kProd && dm.mode != kDebug) {
423 return absl::InvalidArgumentError(
424 absl::StrCat("[DebugMode] invalid mode: ", dm.mode));
425 }
426 return dm;
427 }
428
AsExtension() const429 absl::StatusOr<Extension> ProxyLayer::AsExtension() const {
430 bssl::ScopedCBB cbb;
431 if (!CBB_init(cbb.get(), sizeof(uint8_t))) {
432 return absl::InternalError("CBB_init() failed.");
433 }
434 if (!CBB_add_u8(cbb.get(), layer)) {
435 return absl::InternalError("Failed to add layer to cbb.");
436 }
437 uint8_t* wire_format;
438 size_t wire_format_len;
439 if (!CBB_finish(cbb.get(), &wire_format, &wire_format_len)) {
440 return absl::InternalError("Failed to generate wire format.");
441 }
442 std::string wire_format_str(reinterpret_cast<const char*>(wire_format),
443 wire_format_len);
444 OPENSSL_free(wire_format);
445 return Extension{.extension_type = 0xF003,
446 .extension_value = wire_format_str};
447 }
448
FromExtension(const Extension & ext)449 absl::StatusOr<ProxyLayer> ProxyLayer::FromExtension(const Extension& ext) {
450 if (ext.extension_type != 0xF003) {
451 return absl::InvalidArgumentError(absl::StrCat(
452 "[ProxyLayer] extension of wrong type: ", ext.extension_type));
453 }
454 ProxyLayer pl;
455 CBS cbs;
456 CBS_init(&cbs, reinterpret_cast<const uint8_t*>(ext.extension_value.data()),
457 ext.extension_value.size());
458 if (!CBS_get_u8(&cbs, &pl.layer)) {
459 return absl::InvalidArgumentError(
460 "[ProxyLayer] failed to read len from extension");
461 }
462 if (pl.layer != kProxyA && pl.layer != kProxyB) {
463 return absl::InvalidArgumentError(
464 absl::StrCat("[ProxyLayer] invalid layer: ", pl.layer));
465 }
466 return pl;
467 }
468
DecodeExtensions(absl::string_view encoded_extensions)469 absl::StatusOr<Extensions> DecodeExtensions(
470 absl::string_view encoded_extensions) {
471 CBS cbs;
472 CBS_init(&cbs, reinterpret_cast<const uint8_t*>(encoded_extensions.data()),
473 encoded_extensions.size());
474 CBS extensions_cbs;
475 if (!CBS_get_u16_length_prefixed(&cbs, &extensions_cbs)) {
476 return absl::InvalidArgumentError("failed to read extensions.");
477 }
478 if (CBS_len(&extensions_cbs) == 0) {
479 return absl::InvalidArgumentError("At least one extension is required.");
480 }
481 if (CBS_len(&cbs) != 0) {
482 return absl::InvalidArgumentError("no data after extensions is allowed.");
483 }
484 Extensions extensions;
485 while (CBS_len(&extensions_cbs) > 0) {
486 ANON_TOKENS_ASSIGN_OR_RETURN(const Extension ext,
487 decodeExtension(&extensions_cbs));
488 extensions.extensions.push_back(ext);
489 }
490 return extensions;
491 }
492
MarshalTokenChallenge(const TokenChallenge & token_challenge)493 absl::StatusOr<std::string> MarshalTokenChallenge(
494 const TokenChallenge& token_challenge) {
495 // Main CryptoByteBuilder object cbb which will be passed to CBB_finish to
496 // finalize the output string.
497 bssl::ScopedCBB cbb;
498 // initial_capacity only serves as a hint.
499 if (!CBB_init(cbb.get(), /*initial_capacity=*/98)) {
500 return absl::InternalError("CBB_init() failed.");
501 }
502 // Add token_type to cbb.
503 if (!CBB_add_u16(cbb.get(), token_challenge.token_type)) {
504 return absl::InvalidArgumentError("Could not add token_type to cbb.");
505 }
506 // Add issuer_name to cbb using temporary cbb struct object issuer_name_cbb.
507 CBB issuer_name_cbb;
508 if (!CBB_add_u16_length_prefixed(cbb.get(), &issuer_name_cbb) ||
509 !CBB_add_bytes(
510 &issuer_name_cbb,
511 reinterpret_cast<const uint8_t*>(token_challenge.issuer_name.data()),
512 token_challenge.issuer_name.size())) {
513 return absl::InvalidArgumentError("Could not add issuer_name to cbb.");
514 }
515 uint8_t* marshaled_challenge;
516 size_t marshaled_challenge_len;
517 if (!CBB_finish(cbb.get(), &marshaled_challenge, &marshaled_challenge_len)) {
518 return absl::InvalidArgumentError("Failed to marshal token challenge");
519 }
520 std::string marshaled_challenge_str(
521 reinterpret_cast<const char*>(marshaled_challenge),
522 marshaled_challenge_len);
523 // Free memory.
524 OPENSSL_free(marshaled_challenge);
525 return marshaled_challenge_str;
526 }
527
MarshalTokenRequest(const TokenRequest & token_request)528 absl::StatusOr<std::string> MarshalTokenRequest(
529 const TokenRequest& token_request) {
530 // Main CryptoByteBuilder object cbb which will be passed to CBB_finish to
531 // finalize the output string.
532 bssl::ScopedCBB cbb;
533 // initial_capacity only serves as a hint.
534 if (!CBB_init(cbb.get(),
535 /*initial_capacity=*/kDA7AMarshaledTokenRequestSizeInBytes)) {
536 return absl::InternalError("CBB_init() failed.");
537 }
538 // Add token_type to cbb.
539 if (!CBB_add_u16(cbb.get(), token_request.token_type) ||
540 // Add truncated_token_key_id to cbb.
541 !CBB_add_u8(cbb.get(), token_request.truncated_token_key_id) ||
542 // Add blinded_token_request string to cbb.
543 !CBB_add_bytes(cbb.get(),
544 reinterpret_cast<const uint8_t*>(
545 token_request.blinded_token_request.data()),
546 token_request.blinded_token_request.size())) {
547 return absl::InvalidArgumentError(
548 "Could not construct cbb with given inputs.");
549 }
550
551 uint8_t* encoded_output;
552 size_t encoded_output_len;
553 if (!CBB_finish(cbb.get(), &encoded_output, &encoded_output_len)) {
554 return absl::InvalidArgumentError(
555 "Failed to generate token request encoding");
556 }
557 std::string encoded_output_str(reinterpret_cast<const char*>(encoded_output),
558 encoded_output_len);
559 // Free memory.
560 OPENSSL_free(encoded_output);
561 return encoded_output_str;
562 }
563
UnmarshalTokenRequest(absl::string_view token_request)564 absl::StatusOr<TokenRequest> UnmarshalTokenRequest(
565 absl::string_view token_request) {
566 TokenRequest out;
567 out.blinded_token_request.resize(kDA7ABlindedTokenRequestSizeInBytes);
568 CBS cbs;
569 CBS_init(&cbs, reinterpret_cast<const uint8_t*>(token_request.data()),
570 token_request.size());
571 if (!CBS_get_u16(&cbs, &out.token_type)) {
572 return absl::InvalidArgumentError("failed to read token type");
573 }
574 if (out.token_type != 0xDA7A) {
575 return absl::InvalidArgumentError("unsupported token type");
576 }
577 if (!CBS_get_u8(&cbs, &out.truncated_token_key_id)) {
578 return absl::InvalidArgumentError("failed to read truncated_token_key_id");
579 }
580 if (!CBS_copy_bytes(
581 &cbs, reinterpret_cast<uint8_t*>(out.blinded_token_request.data()),
582 out.blinded_token_request.size())) {
583 return absl::InvalidArgumentError("failed to read blinded_token_request");
584 }
585 if (CBS_len(&cbs) != 0) {
586 return absl::InvalidArgumentError("token request had extra bytes");
587 }
588 return out;
589 }
590
MarshalExtendedTokenRequest(const ExtendedTokenRequest & extended_token_request)591 absl::StatusOr<std::string> MarshalExtendedTokenRequest(
592 const ExtendedTokenRequest& extended_token_request) {
593 // Main CryptoByteBuilder object cbb which will be passed to CBB_finish to
594 // finalize the output string.
595 bssl::ScopedCBB cbb;
596 // Initial_capacity only serves as a hint. Note that
597 // extended_token_request.request would occupy 259 bytes as the token type is
598 // DA7A and we will add some additional bytes as buffer for the extensions.
599 if (!CBB_init(
600 cbb.get(),
601 /*initial_capacity=*/kDA7AMarshaledTokenRequestSizeInBytes + 41)) {
602 return absl::InternalError("CBB_init() failed.");
603 }
604 // Marshal TokenRequest structure.
605 ANON_TOKENS_ASSIGN_OR_RETURN(
606 std::string encoded_request,
607 MarshalTokenRequest(extended_token_request.request));
608 // Marshal Extensions structure.
609 ANON_TOKENS_ASSIGN_OR_RETURN(
610 std::string encoded_extensions,
611 EncodeExtensions(extended_token_request.extensions));
612
613 // Add encoded_request to cbb.
614 if (!CBB_add_bytes(cbb.get(),
615 reinterpret_cast<const uint8_t*>(encoded_request.data()),
616 encoded_request.size()) ||
617 // Add encoded_extensions to cbb.
618 !CBB_add_bytes(
619 cbb.get(),
620 reinterpret_cast<const uint8_t*>(encoded_extensions.data()),
621 encoded_extensions.size())) {
622 return absl::InvalidArgumentError(
623 "Could not construct cbb with given inputs.");
624 }
625
626 uint8_t* encoded_output;
627 size_t encoded_output_len;
628 if (!CBB_finish(cbb.get(), &encoded_output, &encoded_output_len)) {
629 return absl::InvalidArgumentError(
630 "Failed to generate token request encoding");
631 }
632 std::string encoded_output_str(reinterpret_cast<const char*>(encoded_output),
633 encoded_output_len);
634 // Free memory.
635 OPENSSL_free(encoded_output);
636 return encoded_output_str;
637 }
638
UnmarshalExtendedTokenRequest(absl::string_view extended_token_request)639 absl::StatusOr<ExtendedTokenRequest> UnmarshalExtendedTokenRequest(
640 absl::string_view extended_token_request) {
641 CBS cbs;
642 CBS_init(&cbs,
643 reinterpret_cast<const uint8_t*>(extended_token_request.data()),
644 extended_token_request.size());
645
646 std::string encoded_token_request;
647 encoded_token_request.resize(kDA7AMarshaledTokenRequestSizeInBytes);
648 if (!CBS_copy_bytes(&cbs,
649 reinterpret_cast<uint8_t*>(encoded_token_request.data()),
650 encoded_token_request.size())) {
651 return absl::InvalidArgumentError("failed to read encoded_token_request");
652 }
653
654 std::string encoded_extensions;
655 encoded_extensions.resize(CBS_len(&cbs));
656 if (!CBS_copy_bytes(&cbs,
657 reinterpret_cast<uint8_t*>(encoded_extensions.data()),
658 encoded_extensions.size())) {
659 return absl::InvalidArgumentError("failed to read encoded_extensions");
660 }
661
662 ExtendedTokenRequest out;
663 ANON_TOKENS_ASSIGN_OR_RETURN(out.request,
664 UnmarshalTokenRequest(encoded_token_request));
665 ANON_TOKENS_ASSIGN_OR_RETURN(out.extensions,
666 DecodeExtensions(encoded_extensions));
667 return out;
668 }
669
ValidateExtensionsOrderAndValues(const Extensions & extensions,absl::Span<uint16_t> expected_types,absl::Time now)670 absl::Status ValidateExtensionsOrderAndValues(
671 const Extensions& extensions, absl::Span<uint16_t> expected_types,
672 absl::Time now) {
673 if (expected_types.size() != extensions.extensions.size()) {
674 return absl::InvalidArgumentError(
675 absl::StrFormat("Expected %d type, got %d", expected_types.size(),
676 extensions.extensions.size()));
677 }
678 for (size_t i = 0; i < expected_types.size(); i++) {
679 if (expected_types[i] != extensions.extensions[i].extension_type) {
680 return absl::InvalidArgumentError(absl::StrFormat(
681 "Expected %x type at index %d, got %x", expected_types[i], i,
682 extensions.extensions[i].extension_type));
683 }
684 }
685 return ValidateExtensionsValues(extensions, now);
686 }
687
ValidateExtensionsValues(const Extensions & extensions,absl::Time now)688 absl::Status ValidateExtensionsValues(const Extensions& extensions,
689 absl::Time now) {
690 for (const Extension& ext : extensions.extensions) {
691 switch (ext.extension_type) {
692 case 0x0001: {
693 absl::StatusOr<ExpirationTimestamp> expiration_timestamp =
694 ExpirationTimestamp::FromExtension(ext);
695 if (!expiration_timestamp.ok()) {
696 return expiration_timestamp.status();
697 }
698 if (expiration_timestamp->timestamp % kFifteenMinutesInSeconds != 0) {
699 return absl::InvalidArgumentError(
700 "Expiration timestamp is not rounded");
701 }
702 absl::Time timestamp =
703 absl::FromUnixSeconds(expiration_timestamp->timestamp);
704 if (timestamp < now || timestamp > now + absl::Hours(kOneWeekToHours)) {
705 return absl::InvalidArgumentError(
706 "Expiration timestamp is out of range");
707 }
708 break;
709 }
710 case 0x0002: {
711 absl::StatusOr<GeoHint> geo_hint = GeoHint::FromExtension(ext);
712 if (!geo_hint.ok()) {
713 return geo_hint.status();
714 }
715 if (geo_hint->country_code.length() != kAlpha2CountryCodeLength) {
716 return absl::InvalidArgumentError("Country code is not 2 characters");
717 }
718 for (const char& c : geo_hint->country_code) {
719 if (!absl::ascii_isupper(c)) {
720 return absl::InvalidArgumentError("Country code is not uppercase");
721 }
722 }
723 for (const char& c : geo_hint->region) {
724 if (!absl::ascii_isupper(c) && !absl::ascii_ispunct(c)) {
725 return absl::InvalidArgumentError("Region is not uppercase");
726 }
727 }
728 break;
729 }
730 case 0xF001: {
731 absl::StatusOr<ServiceType> service_type =
732 ServiceType::FromExtension(ext);
733 if (!service_type.ok()) {
734 return service_type.status();
735 }
736 break;
737 }
738 case 0xF002: {
739 absl::StatusOr<DebugMode> debug_mode = DebugMode::FromExtension(ext);
740 if (!debug_mode.ok()) {
741 return debug_mode.status();
742 }
743 break;
744 }
745 case 0xF003: {
746 absl::StatusOr<ProxyLayer> proxy_layer = ProxyLayer::FromExtension(ext);
747 if (!proxy_layer.ok()) {
748 return proxy_layer.status();
749 }
750 break;
751 }
752 default: {
753 return absl::InvalidArgumentError("Unsupported extension type");
754 }
755 }
756 }
757 return absl::OkStatus();
758 }
759
760 } // namespace anonymous_tokens
761