Use one of 7 sizes for Bidding and Auction Services API requests

Limit Bidding and Auction Services API encrypted requests to one of the
following 7 sizes: [0, 5, 10, 20, 30, 40, 55] kB. Requests will be
padded up to these lengths when a specific size is not specified. Note
that this also sets a maximum size for the encrypted request instead of
using arbitrarily large powers of 2.

This is necessary to limit the side channel where the size of the
encrypted blob can leak information about the presence of interest
groups on device. With 7 potential sizes, we limit the leak to less than
3 bits as a trade-off between privacy and utility. Previously we were
padding up to powers of 2 in size but had no max size so the encrypted
blob size could potentially leak relatively large amounts of
information, limited only by the maximum size for interest groups.

Change-Id: I05a5b405c7f46c6130974fdc56b829abc0908db2
Fixed: 343719161
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5577617
Reviewed-by: Caleb Raitto <caraitto@chromium.org>
Commit-Queue: Russ Hamilton <behamilton@google.com>
Cr-Commit-Position: refs/heads/main@{#1311307}
diff --git a/content/browser/interest_group/ad_auction_service_impl_unittest.cc b/content/browser/interest_group/ad_auction_service_impl_unittest.cc
index ede27f5..92b292f 100644
--- a/content/browser/interest_group/ad_auction_service_impl_unittest.cc
+++ b/content/browser/interest_group/ad_auction_service_impl_unittest.cc
@@ -100,6 +100,8 @@
 using RealTimeReportingType =
     blink::mojom::AuctionAdConfigNonSharedParams_RealTimeReportingType;
 
+const size_t kEncryptionOverhead = 56;
+
 constexpr char kInterestGroupName[] = "interest-group-name";
 constexpr char kOriginStringA[] = "https://a.test";
 constexpr char kOriginStringB[] = "https://b.test";
@@ -11295,11 +11297,9 @@
       "gCb7U68hruRHrkQd7VXoQQk0C9Kz5iHTtpJ2SjdTiwW4+wc5+"
       "OUq8Ay6ql6RmQpfocD9RbxQn3yRaqdke7/"
       "Cv94fgB8SY7doAAAAHRlbmFibGVEZWJ1Z1JlcG9ydGluZ/"
-      "UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
-      "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
-      "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
-      "AAAAAAAAAA";
-  EXPECT_EQ(expected, base::Base64Encode(msg));
+      "UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
+  EXPECT_THAT(base::Base64Encode(msg), testing::StartsWith(expected));
+  EXPECT_EQ(5u * 1024u - kEncryptionOverhead, msg.size());
   EXPECT_THAT(group_names, testing::ElementsAre(testing::Pair(
                                test_origin, testing::ElementsAre("cars"))));
 }
@@ -11453,7 +11453,8 @@
       "RlzQJCsXrtHpPKbqR3XuCedixKnuDfbZw4DPJCaWJW2h4M+3ASxj+2Pxv+"
       "8zJsAAAAdGVuYWJsZURlYnVnUmVwb3J0aW5n9QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
       "AAAAAAAAAAAAAAAA";
-  EXPECT_EQ(expected, base::Base64Encode(msg));
+  EXPECT_THAT(base::Base64Encode(msg), testing::StartsWith(expected));
+  EXPECT_EQ(5u * 1024u - kEncryptionOverhead, msg.size());
   EXPECT_THAT(
       group_names,
       testing::UnorderedElementsAre(
@@ -11497,11 +11498,9 @@
       "U7wkvSg1OTUvuZIxIykzxTm/"
       "NK+EIaOgKLUsPDOvuCEzKz8zDyzICAC+EO2LTgAAAHRlbmFibGVEZWJ1Z1JlcG9ydGluZ/"
       "QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
-      "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
-      "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
-      "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
       "AAAAAAAAAA";
-  EXPECT_EQ(expected, base::Base64Encode(msg));
+  EXPECT_THAT(base::Base64Encode(msg), testing::StartsWith(expected));
+  EXPECT_EQ(5u * 1024u - kEncryptionOverhead, msg.size());
   EXPECT_THAT(group_names, testing::ElementsAre(testing::Pair(
                                test_origin, testing::ElementsAre("cars"))));
 }
@@ -11541,17 +11540,21 @@
         run_loop.Quit();
       }));
   run_loop.Run();
-  EXPECT_EQ(
-      "AgAAARmmZ3ZlcnNpb24AaXB1Ymxpc2hlcmZhLnRlc3RsZ2VuZXJhdGlvbklkeCQwMDAwMDAw"
-      "MC0wMDAwLTAwMDAtMDAwMC0wMDAwMDAwMDAwMDBuaW50ZXJlc3RHcm91cHOhbmh0dHBzOi8v"
-      "YS50ZXN0WGMfiwgAAAAAAAAAa1ycnJhS3JRiaGRskmxiapaSl5ibmpKcWFScl1SUX16cWhSc"
-      "mZ6XmFO8JL0oNTk1L7mSMSMpM8U5vzSvhCGjoCi1LDwzr7ghMys/"
-      "Mw8syAgABWZNKFIAAAB0Y29uc2VudGVkRGVidWdDb25maWeiZXRva2VuZ215VG9rZW5raXND"
-      "b25zZW50ZWT1dGVuYWJsZURlYnVnUmVwb3J0aW5n9QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
-      "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
-      "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
-      "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
-      base::Base64Encode(msg));
+  EXPECT_THAT(base::Base64Encode(msg),
+              testing::StartsWith(
+                  "AgAAARmmZ3ZlcnNpb24AaXB1Ymxpc2hlcmZhLnRlc3RsZ2VuZXJhdGlvbklk"
+                  "eCQwMDAwMDAw"
+                  "MC0wMDAwLTAwMDAtMDAwMC0wMDAwMDAwMDAwMDBuaW50ZXJlc3RHcm91cHOh"
+                  "bmh0dHBzOi8v"
+                  "YS50ZXN0WGMfiwgAAAAAAAAAa1ycnJhS3JRiaGRskmxiapaSl5ibmpKcWFSc"
+                  "l1SUX16cWhSc"
+                  "mZ6XmFO8JL0oNTk1L7mSMSMpM8U5vzSvhCGjoCi1LDwzr7ghMys/"
+                  "Mw8syAgABWZNKFIAAAB0Y29uc2VudGVkRGVidWdDb25maWeiZXRva2VuZ215"
+                  "VG9rZW5raXND"
+                  "b25zZW50ZWT1dGVuYWJsZURlYnVnUmVwb3J0aW5n9QAAAAAAAAAAAAAAAAAA"
+                  "AAAAAAAAAAAA"
+                  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"));
+  EXPECT_EQ(5u * 1024u - kEncryptionOverhead, msg.size());
   EXPECT_THAT(group_names, testing::ElementsAre(testing::Pair(
                                test_origin, testing::ElementsAre("cars"))));
 }
@@ -11600,11 +11603,9 @@
       "5kikjKTPFOb80r4Qho6AotSw8M6+"
       "4qYkhoYkxxdDI2CQzKz8zDyzNCAAKnN0ZTgAAAHRlbmFibGVEZWJ1Z1JlcG9ydGluZ/"
       "UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
-      "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
-      "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
-      "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
       "AAAAAAAAAA";
-  EXPECT_EQ(expected, base::Base64Encode(msg));
+  EXPECT_THAT(base::Base64Encode(msg), testing::StartsWith(expected));
+  EXPECT_EQ(5u * 1024u - kEncryptionOverhead, msg.size());
   EXPECT_THAT(group_names, testing::ElementsAre(testing::Pair(
                                test_origin, testing::ElementsAre("cars"))));
 }
@@ -11662,7 +11663,8 @@
       "dSZXBvcnRpbmf1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
       "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
       "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
-  EXPECT_EQ(expected, base::Base64Encode(msg));
+  EXPECT_THAT(base::Base64Encode(msg), testing::StartsWith(expected));
+  EXPECT_EQ(5u * 1024u - kEncryptionOverhead, msg.size());
   EXPECT_THAT(group_names, testing::ElementsAre(testing::Pair(
                                test_origin, testing::ElementsAre("cars"))));
 }
@@ -11750,7 +11752,7 @@
     blink::mojom::AuctionDataConfigPtr config =
         blink::mojom::AuctionDataConfig::New();
     // All groups require 418 bytes, so less than that (plus framing overhead).
-    config->request_size = 412 + 56;
+    config->request_size = 412 + kEncryptionOverhead;
     config->per_buyer_configs.emplace(
         test_origin_a, blink::mojom::AuctionDataBuyerConfig::New(/*size=*/256));
     config->per_buyer_configs.emplace(
@@ -11764,7 +11766,7 @@
         /*generation_id=*/
         base::Uuid::ParseCaseInsensitive(
             "00000000-0000-0000-0000-000000000000"),
-        /*config=*/std::move(config),
+        /*config=*/config->Clone(),
         /*callback=*/
         base::BindLambdaForTesting([&](BiddingAndAuctionData data) {
           msg = std::move(data.request);
@@ -11783,8 +11785,9 @@
         "JMoIjFCkfofEEEflObJTUP+azsKgFeXrTnf18C7Ydx7VMboLtwC30lxOt+"
         "RlzQJCsXrtHpPKbqR3XuCedixKnuDfbZw4DPJCaWJW2h4M+3ASxj+2Pxv+"
         "8zJsAAAAdGVuYWJsZURlYnVnUmVwb3J0aW5n9QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
-        "AAAAAAAAAAAA==";
-    EXPECT_EQ(expected, base::Base64Encode(msg));
+        "AAAAAAAAAAAA";
+    EXPECT_THAT(base::Base64Encode(msg), testing::StartsWith(expected));
+    EXPECT_EQ(*config->request_size - kEncryptionOverhead, msg.size());
     EXPECT_THAT(
         group_names,
         testing::UnorderedElementsAre(
@@ -11797,7 +11800,7 @@
     blink::mojom::AuctionDataConfigPtr config =
         blink::mojom::AuctionDataConfig::New();
     // All groups require 418 bytes, so less than that.
-    config->request_size = 412 + 56;
+    config->request_size = 412 + kEncryptionOverhead;
     config->per_buyer_configs.emplace(
         test_origin_a, blink::mojom::AuctionDataBuyerConfig::New(/*size=*/256));
     config->per_buyer_configs.emplace(
@@ -11811,7 +11814,7 @@
         /*generation_id=*/
         base::Uuid::ParseCaseInsensitive(
             "00000000-0000-0000-0000-000000000000"),
-        /*config=*/std::move(config),
+        /*config=*/config->Clone(),
         /*callback=*/
         base::BindLambdaForTesting([&](BiddingAndAuctionData data) {
           msg = std::move(data.request);
@@ -11827,10 +11830,9 @@
         "sOimQKhATNFDPSvDgjWI10EAhFytIy9ERGIGiH0g/CxEGfaazYeJmU/"
         "Mk1DFedG09IjPesNc4e5cZh0Q6exrNjfbhOHKdy+WZsUVY11utNMiyC/yJwu9v/A/"
         "IfQIyrS8zT6YfKXmqteTfTAAAAdGVuYWJsZURlYnVnUmVwb3J0aW5n9QAAAAAAAAAAAAAA"
-        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
-        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
-        "AAAAAAAAAA==";
-    EXPECT_EQ(expected, base::Base64Encode(msg));
+        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
+    EXPECT_THAT(base::Base64Encode(msg), testing::StartsWith(expected));
+    EXPECT_EQ(*config->request_size - kEncryptionOverhead, msg.size());
     EXPECT_THAT(group_names,
                 testing::UnorderedElementsAre(testing::Pair(
                     test_origin_a, testing::ElementsAre("boats", "cars"))));
@@ -11841,7 +11843,7 @@
     blink::mojom::AuctionDataConfigPtr config =
         blink::mojom::AuctionDataConfig::New();
     // All groups require 418 bytes, so less than that.
-    config->request_size = 412 + 56;
+    config->request_size = 412 + kEncryptionOverhead;
     config->per_buyer_configs.emplace(
         test_origin_a, blink::mojom::AuctionDataBuyerConfig::New());
     // Buyer B requires 129 bytes.
@@ -11856,7 +11858,7 @@
         /*generation_id=*/
         base::Uuid::ParseCaseInsensitive(
             "00000000-0000-0000-0000-000000000000"),
-        /*config=*/std::move(config),
+        /*config=*/config->Clone(),
         /*callback=*/
         base::BindLambdaForTesting([&](BiddingAndAuctionData data) {
           msg = std::move(data.request);
@@ -11873,9 +11875,9 @@
         "Mk1DFedG09IjPesNc4e5cZh0Q6exrNjfbhOHKdy+WZsUVY11utNMiyC/yJwu9v/A/"
         "IfQIyrS8zT6YfKXmqteTfTAAAAdGVuYWJsZURlYnVnUmVwb3J0aW5n9QAAAAAAAAAAAAAA"
         "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
-        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
-        "AAAAAAAAAA==";
-    EXPECT_EQ(expected, base::Base64Encode(msg));
+        "AAAAAAAAAAAA";
+    EXPECT_THAT(base::Base64Encode(msg), testing::StartsWith(expected));
+    EXPECT_EQ(*config->request_size - kEncryptionOverhead, msg.size());
     EXPECT_THAT(group_names,
                 testing::UnorderedElementsAre(testing::Pair(
                     test_origin_a, testing::ElementsAre("boats", "cars"))));
@@ -11886,7 +11888,7 @@
     blink::mojom::AuctionDataConfigPtr config =
         blink::mojom::AuctionDataConfig::New();
     // All groups require 418 bytes, so less than that.
-    config->request_size = 412 + 56;
+    config->request_size = 412 + kEncryptionOverhead;
     config->per_buyer_configs.emplace(
         test_origin_a, blink::mojom::AuctionDataBuyerConfig::New());
     config->per_buyer_configs.emplace(
@@ -11900,7 +11902,7 @@
         /*generation_id=*/
         base::Uuid::ParseCaseInsensitive(
             "00000000-0000-0000-0000-000000000000"),
-        /*config=*/std::move(config),
+        /*config=*/config->Clone(),
         /*callback=*/
         base::BindLambdaForTesting([&](BiddingAndAuctionData data) {
           msg = std::move(data.request);
@@ -11918,9 +11920,9 @@
         "8tsENWFTR83JFlbjNoAAAAbmh0dHBzOi8vYi50ZXN0WHAfiwgAAAAAAAAAJYnRDYMwDAVh"
         "JMoIjFCkfofEEEflObJTUP+azsKgFeXrTnf18C7Ydx7VMboLtwC30lxOt+"
         "RlzQJCsXrtHpPKbqR3XuCedixKnuDfbZw4DPJCaWJW2h4M+3ASxj+2Pxv+"
-        "8zJsAAAAdGVuYWJsZURlYnVnUmVwb3J0aW5n9QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
-        "AAAAAAAAAAAA==";
-    EXPECT_EQ(expected, base::Base64Encode(msg));
+        "8zJsAAAAdGVuYWJsZURlYnVnUmVwb3J0aW5n9QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
+    EXPECT_THAT(base::Base64Encode(msg), testing::StartsWith(expected));
+    EXPECT_EQ(*config->request_size - kEncryptionOverhead, msg.size());
     EXPECT_THAT(
         group_names,
         testing::UnorderedElementsAre(
@@ -11933,7 +11935,7 @@
     blink::mojom::AuctionDataConfigPtr config =
         blink::mojom::AuctionDataConfig::New();
     // All groups require 418 bytes, so less than that.
-    config->request_size = 412 + 56;
+    config->request_size = 412 + kEncryptionOverhead;
     config->per_buyer_configs.emplace(
         test_origin_a, blink::mojom::AuctionDataBuyerConfig::New(/*size=*/512));
     config->per_buyer_configs.emplace(
@@ -11947,7 +11949,7 @@
         /*generation_id=*/
         base::Uuid::ParseCaseInsensitive(
             "00000000-0000-0000-0000-000000000000"),
-        /*config=*/std::move(config),
+        /*config=*/config->Clone(),
         /*callback=*/
         base::BindLambdaForTesting([&](BiddingAndAuctionData data) {
           msg = std::move(data.request);
@@ -11963,10 +11965,9 @@
         "sOimQKhATNFDPSvDgjWI10EAhFytIy9ERGIGiH0g/CxEGfaazYeJmU/"
         "Mk1DFedG09IjPesNc4e5cZh0Q6exrNjfbhOHKdy+WZsUVY11utNMiyC/yJwu9v/A/"
         "IfQIyrS8zT6YfKXmqteTfTAAAAdGVuYWJsZURlYnVnUmVwb3J0aW5n9QAAAAAAAAAAAAAA"
-        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
-        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
-        "AAAAAAAAAA==";
-    EXPECT_EQ(expected, base::Base64Encode(msg));
+        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
+    EXPECT_THAT(base::Base64Encode(msg), testing::StartsWith(expected));
+    EXPECT_EQ(*config->request_size - kEncryptionOverhead, msg.size());
     EXPECT_THAT(group_names,
                 testing::UnorderedElementsAre(testing::Pair(
                     test_origin_a, testing::ElementsAre("boats", "cars"))));
@@ -11988,7 +11989,7 @@
         /*generation_id=*/
         base::Uuid::ParseCaseInsensitive(
             "00000000-0000-0000-0000-000000000000"),
-        /*config=*/std::move(config),
+        /*config=*/config->Clone(),
         /*callback=*/
         base::BindLambdaForTesting([&](BiddingAndAuctionData data) {
           msg = std::move(data.request);
@@ -12004,20 +12005,9 @@
         "sOimQKhATNFDPSvDgjWI10EAhFytIy9ERGIGiH0g/CxEGfaazYeJmU/"
         "Mk1DFedG09IjPesNc4e5cZh0Q6exrNjfbhOHKdy+WZsUVY11utNMiyC/yJwu9v/A/"
         "IfQIyrS8zT6YfKXmqteTfTAAAAdGVuYWJsZURlYnVnUmVwb3J0aW5n9QAAAAAAAAAAAAAA"
-        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
-        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
-        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
-        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
-        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
-        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
-        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
-        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
-        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
-        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
-        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
-        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
-        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
-    EXPECT_EQ(expected, base::Base64Encode(msg));
+        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
+    EXPECT_THAT(base::Base64Encode(msg), testing::StartsWith(expected));
+    EXPECT_EQ(*config->request_size - kEncryptionOverhead, msg.size());
     EXPECT_THAT(group_names,
                 testing::UnorderedElementsAre(testing::Pair(
                     test_origin_a, testing::ElementsAre("boats", "cars"))));
@@ -12028,7 +12018,7 @@
   {
     blink::mojom::AuctionDataConfigPtr config =
         blink::mojom::AuctionDataConfig::New();
-    config->request_size = 381 + 56;
+    config->request_size = 381 + kEncryptionOverhead;
 
     std::vector<uint8_t> msg;
     base::flat_map<url::Origin, std::vector<std::string>> group_names;
@@ -12038,7 +12028,7 @@
         /*generation_id=*/
         base::Uuid::ParseCaseInsensitive(
             "00000000-0000-0000-0000-000000000000"),
-        /*config=*/std::move(config),
+        /*config=*/config->Clone(),
         /*callback=*/
         base::BindLambdaForTesting([&](BiddingAndAuctionData data) {
           msg = std::move(data.request);
@@ -12058,6 +12048,7 @@
         "RlzQJCsXrtHpPKbqR3XuCedixKnuDfbZw4DPJCaWJW2h4M+3ASxj+2Pxv+"
         "8zJsAAAAdGVuYWJsZURlYnVnUmVwb3J0aW5n9QAA";
     EXPECT_EQ(expected, base::Base64Encode(msg));
+    EXPECT_EQ(*config->request_size - kEncryptionOverhead, msg.size());
     EXPECT_THAT(
         group_names,
         testing::UnorderedElementsAre(
@@ -12070,7 +12061,7 @@
   {
     blink::mojom::AuctionDataConfigPtr config =
         blink::mojom::AuctionDataConfig::New();
-    config->request_size = 20 + 56;
+    config->request_size = 20 + kEncryptionOverhead;
 
     std::vector<uint8_t> msg;
     base::flat_map<url::Origin, std::vector<std::string>> group_names;
@@ -12080,7 +12071,7 @@
         /*generation_id=*/
         base::Uuid::ParseCaseInsensitive(
             "00000000-0000-0000-0000-000000000000"),
-        /*config=*/std::move(config),
+        /*config=*/config->Clone(),
         /*callback=*/
         base::BindLambdaForTesting([&](BiddingAndAuctionData data) {
           msg = std::move(data.request);
@@ -12192,9 +12183,6 @@
   ASSERT_TRUE(result.has_value());
   ASSERT_LT(0u, result.value().request.size());
 
-  // The message should be a power of 2 in length
-  EXPECT_EQ(1, absl::popcount(result->request.size()));
-
   auto key_config = quiche::ObliviousHttpHeaderKeyConfig::Create(
                         0x12, EVP_HPKE_DHKEM_X25519_HKDF_SHA256,
                         EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_256_GCM)
@@ -12373,8 +12361,8 @@
               blink::FencedFrame::ReportingDestination::kComponentSeller,
               testing::ElementsAre())));
 
-  // Request should be padded to 512 bytes.
-  const size_t kExpectedBaDataSize = 512;
+  // Request should be padded to 5k bytes.
+  const size_t kExpectedBaDataSize = 5 * 1024;
   hist.ExpectUniqueSample("Ads.InterestGroup.BaDataSize", kExpectedBaDataSize,
                           1);
   hist.ExpectTotalCount("Ads.InterestGroup.BaDataConstructionTime", 1);
@@ -12476,8 +12464,8 @@
       main_rfh());
   EXPECT_FALSE(result);
 
-  // Request should be padded to 512 bytes.
-  const size_t kExpectedBaDataSize = 512;
+  // Request should be padded to 5k bytes.
+  const size_t kExpectedBaDataSize = 5 * 1024;
   hist.ExpectUniqueSample("Ads.InterestGroup.BaDataSize", kExpectedBaDataSize,
                           1);
   hist.ExpectTotalCount("Ads.InterestGroup.BaDataConstructionTime", 1);
@@ -12564,8 +12552,8 @@
       main_rfh());
   EXPECT_FALSE(result);
 
-  // Request should be padded to 512 bytes.
-  const size_t kExpectedBaDataSize = 512;
+  // Request should be padded to 5k bytes.
+  const size_t kExpectedBaDataSize = 5 * 1024;
   hist.ExpectUniqueSample("Ads.InterestGroup.BaDataSize", kExpectedBaDataSize,
                           1);
   hist.ExpectTotalCount("Ads.InterestGroup.BaDataConstructionTime", 1);
@@ -12635,9 +12623,6 @@
   EXPECT_TRUE(request.ok()) << request.status();
   auto plaintext_data = request->GetPlaintextData();
 
-  // The message should be a power of 2 in length
-  EXPECT_EQ(1, absl::popcount(auction_data->request.size()));
-
   EXPECT_EQ(0x02, plaintext_data[0]);
   size_t request_size = 0;
   for (size_t idx = 0; idx < sizeof(uint32_t); idx++) {
@@ -12724,8 +12709,8 @@
               blink::FencedFrame::ReportingDestination::kComponentSeller,
               testing::ElementsAre())));
 
-  // Request should be padded to 512 bytes.
-  const size_t kExpectedBaDataSize = 512;
+  // Request should be padded to 5k bytes.
+  const size_t kExpectedBaDataSize = 5 * 1024;
   hist.ExpectUniqueSample("Ads.InterestGroup.BaDataSize", kExpectedBaDataSize,
                           1);
   hist.ExpectTotalCount("Ads.InterestGroup.BaDataConstructionTime", 1);
@@ -12797,8 +12782,8 @@
 
   EXPECT_EQ(network_responder_->ReportCount(), 0u);
 
-  // Request should be padded to 512 bytes.
-  const size_t kExpectedBaDataSize = 512;
+  // Request should be padded to 5k bytes.
+  const size_t kExpectedBaDataSize = 5 * 1024;
   hist.ExpectUniqueSample("Ads.InterestGroup.BaDataSize", kExpectedBaDataSize,
                           1);
   hist.ExpectTotalCount("Ads.InterestGroup.BaDataConstructionTime", 1);
@@ -12881,8 +12866,8 @@
 
   EXPECT_EQ(network_responder_->ReportCount(), 0u);
 
-  // Request should be padded to 512 bytes.
-  const size_t kExpectedBaDataSize = 512;
+  // Request should be padded to 5k bytes.
+  const size_t kExpectedBaDataSize = 5 * 1024;
   hist.ExpectUniqueSample("Ads.InterestGroup.BaDataSize", kExpectedBaDataSize,
                           1);
   hist.ExpectTotalCount("Ads.InterestGroup.BaDataConstructionTime", 1);
@@ -12984,8 +12969,8 @@
 
   EXPECT_EQ(network_responder_->ReportCount(), 0u);
 
-  // Request should be padded to 512 bytes.
-  const size_t kExpectedBaDataSize = 512;
+  // Request should be padded to 5k bytes.
+  const size_t kExpectedBaDataSize = 5 * 1024;
   hist.ExpectUniqueSample("Ads.InterestGroup.BaDataSize", kExpectedBaDataSize,
                           1);
   hist.ExpectTotalCount("Ads.InterestGroup.BaDataConstructionTime", 1);
@@ -13073,8 +13058,8 @@
 
   EXPECT_EQ(network_responder_->ReportCount(), 0u);
 
-  // Request should be padded to 512 bytes.
-  const size_t kExpectedBaDataSize = 512;
+  // Request should be padded to 5k bytes.
+  const size_t kExpectedBaDataSize = 5 * 1024;
   hist.ExpectUniqueSample("Ads.InterestGroup.BaDataSize", kExpectedBaDataSize,
                           1);
   hist.ExpectTotalCount("Ads.InterestGroup.BaDataConstructionTime", 1);
@@ -13201,8 +13186,8 @@
           testing::Pair(blink::FencedFrame::ReportingDestination::kSeller,
                         testing::ElementsAre())));
 
-  // Request should be padded to 512 bytes.
-  const size_t kExpectedBaDataSize = 512;
+  // Request should be padded to 5k bytes.
+  const size_t kExpectedBaDataSize = 5 * 1024;
   hist.ExpectUniqueSample("Ads.InterestGroup.BaDataSize", kExpectedBaDataSize,
                           1);
   hist.ExpectTotalCount("Ads.InterestGroup.BaDataConstructionTime", 1);
@@ -13372,8 +13357,8 @@
           testing::Pair(blink::FencedFrame::ReportingDestination::kSeller,
                         testing::ElementsAre())));
 
-  // Request should be padded to 512 bytes.
-  const size_t kExpectedBaDataSize = 512;
+  // Request should be padded to 5k bytes.
+  const size_t kExpectedBaDataSize = 5 * 1024;
   hist.ExpectUniqueSample("Ads.InterestGroup.BaDataSize", kExpectedBaDataSize,
                           1);
   hist.ExpectTotalCount("Ads.InterestGroup.BaDataConstructionTime", 1);
@@ -13545,8 +13530,8 @@
           testing::Pair(blink::FencedFrame::ReportingDestination::kSeller,
                         testing::ElementsAre())));
 
-  // Request should be padded to 512 bytes.
-  const size_t kExpectedBaDataSize = 512;
+  // Request should be padded to 5k bytes.
+  const size_t kExpectedBaDataSize = 5 * 1024;
   hist.ExpectUniqueSample("Ads.InterestGroup.BaDataSize", kExpectedBaDataSize,
                           1);
   hist.ExpectTotalCount("Ads.InterestGroup.BaDataConstructionTime", 1);
@@ -13680,8 +13665,8 @@
           testing::Pair(blink::FencedFrame::ReportingDestination::kSeller,
                         testing::ElementsAre())));
 
-  // Request should be padded to 512 bytes.
-  const size_t kExpectedBaDataSize = 512;
+  // Request should be padded to 5k bytes.
+  const size_t kExpectedBaDataSize = 5 * 1024;
   hist.ExpectUniqueSample("Ads.InterestGroup.BaDataSize", kExpectedBaDataSize,
                           1);
   hist.ExpectTotalCount("Ads.InterestGroup.BaDataConstructionTime", 1);
@@ -13822,8 +13807,8 @@
           testing::Pair(blink::FencedFrame::ReportingDestination::kSeller,
                         testing::ElementsAre())));
 
-  // Request should be padded to 512 bytes.
-  const size_t kExpectedBaDataSize = 512;
+  // Request should be padded to 5k bytes.
+  const size_t kExpectedBaDataSize = 5 * 1024;
   hist.ExpectUniqueSample("Ads.InterestGroup.BaDataSize", kExpectedBaDataSize,
                           1);
   hist.ExpectTotalCount("Ads.InterestGroup.BaDataConstructionTime", 1);
@@ -14217,8 +14202,8 @@
           testing::Pair(blink::FencedFrame::ReportingDestination::kSeller,
                         testing::ElementsAre())));
 
-  // Request should be padded to 512 bytes.
-  const size_t kExpectedBaDataSize = 512;
+  // Request should be padded to 5k bytes.
+  const size_t kExpectedBaDataSize = 5 * 1024;
   hist.ExpectUniqueSample("Ads.InterestGroup.BaDataSize", kExpectedBaDataSize,
                           1);
   hist.ExpectTotalCount("Ads.InterestGroup.BaDataConstructionTime", 1);
@@ -14317,8 +14302,8 @@
       main_rfh());
   ASSERT_FALSE(result);
 
-  // Request should be padded to 512 bytes.
-  const size_t kExpectedBaDataSize = 512;
+  // Request should be padded to 5k bytes.
+  const size_t kExpectedBaDataSize = 5 * 1024;
   hist.ExpectUniqueSample("Ads.InterestGroup.BaDataSize", kExpectedBaDataSize,
                           1);
   hist.ExpectTotalCount("Ads.InterestGroup.BaDataConstructionTime", 1);
@@ -14417,8 +14402,8 @@
       main_rfh());
   ASSERT_FALSE(result);
 
-  // Request should be padded to 512 bytes.
-  const size_t kExpectedBaDataSize = 512;
+  // Request should be padded to 5k bytes.
+  const size_t kExpectedBaDataSize = 5 * 1024;
   hist.ExpectUniqueSample("Ads.InterestGroup.BaDataSize", kExpectedBaDataSize,
                           1);
   hist.ExpectTotalCount("Ads.InterestGroup.BaDataConstructionTime", 1);
@@ -14518,8 +14503,8 @@
       main_rfh());
   ASSERT_FALSE(result);
 
-  // Request should be padded to 512 bytes.
-  const size_t kExpectedBaDataSize = 512;
+  // Request should be padded to 5k bytes.
+  const size_t kExpectedBaDataSize = 5 * 1024;
   hist.ExpectUniqueSample("Ads.InterestGroup.BaDataSize", kExpectedBaDataSize,
                           1);
   hist.ExpectTotalCount("Ads.InterestGroup.BaDataConstructionTime", 1);
@@ -14659,7 +14644,7 @@
                   GURL(
                       "https://e.test/topLevelSellerInteractionReporting"))))));
 
-  const size_t kExpectedBaDataSize = 512;
+  const size_t kExpectedBaDataSize = 5 * 1024;
   hist.ExpectUniqueSample("Ads.InterestGroup.BaDataSize", kExpectedBaDataSize,
                           1);
   hist.ExpectTotalCount("Ads.InterestGroup.BaDataConstructionTime", 1);
diff --git a/content/browser/interest_group/bidding_and_auction_serializer.cc b/content/browser/interest_group/bidding_and_auction_serializer.cc
index 55c776fa..2152bff 100644
--- a/content/browser/interest_group/bidding_and_auction_serializer.cc
+++ b/content/browser/interest_group/bidding_and_auction_serializer.cc
@@ -5,6 +5,7 @@
 #include "content/browser/interest_group/bidding_and_auction_serializer.h"
 
 #include <algorithm>
+#include <array>
 #include <set>
 #include <string>
 #include <vector>
@@ -34,8 +35,8 @@
 
 namespace {
 
-const size_t kFramingHeaderSize = 5;  // bytes
-const size_t kOhttpEncIdSize = 7;     // bytes
+const size_t kFramingHeaderSize = 5;       // bytes
+const size_t kOhttpEncIdSize = 7;          // bytes
 const size_t kOhttpSharedSecretSize = 48;  // bytes
 const size_t kOhttpHeaderSize = kOhttpEncIdSize + kOhttpSharedSecretSize;
 
@@ -44,6 +45,11 @@
 const uint8_t kGzipCompression = 2;
 const uint8_t kCompressionBitOffset = 0;
 
+// The 7 sizes we are allowed to use when the request size isn't explicitly
+// specified.
+const std::array<uint32_t, 7> kBinSizes = {
+    {0, 5 * 1024, 10 * 1024, 20 * 1024, 30 * 1024, 40 * 1024, 55 * 1024}};
+
 struct ValueAndSize {
   cbor::Value value;
   base::CheckedNumeric<size_t> size;
@@ -840,7 +846,9 @@
 
 BiddingAndAuctionData BiddingAndAuctionSerializer::Build() {
   DCHECK(config_);
-  if (accumulated_groups_.empty()) {
+  // If we are serializing all groups then we can return an empty list.
+  // Otherwise we still need to return a fixed size request (all padding).
+  if (config_->per_buyer_configs.empty() && accumulated_groups_.empty()) {
     return {};
   }
 
@@ -913,17 +921,25 @@
   }
 
   // If we don't fit in the desired size, don't send anything.
-  if (config_->request_size &&
-      total_size_before_groups.ValueOrDie() > config_->request_size.value()) {
+  if (total_size_before_groups.ValueOrDie() >
+      config_->request_size.value_or(kBinSizes.back())) {
     return {};
   }
 
+  blink::mojom::AuctionDataConfigPtr config = config_->Clone();
+  if (!config->request_size) {
+    // If size isn't specified, then we need to fit in the biggest bin.
+    config->request_size = kBinSizes.back();
+  }
+
   SerializedBiddersMap groups = SerializeBidderGroupsWithConfig(
-      accumulated_groups_, *config_, total_size_before_groups.ValueOrDie(),
+      accumulated_groups_, *config, total_size_before_groups.ValueOrDie(),
       start_time_);
 
-  // If we have no groups, don't send anything.
-  if (groups.bidders.empty()) {
+  // If we have no groups and the buyers weren't specified, don't send anything.
+  // We still need to provide a non-empty request if the buyers are specified in
+  // order to avoid leaking interest groups state.
+  if (config->per_buyer_configs.empty() && groups.bidders.empty()) {
     return {};
   }
 
@@ -933,12 +949,13 @@
       cbor::Value(std::move(groups.bidders));
 
   // UMA requires integers, so we scale the relative compressed size by 100.
-  CHECK_NE(0u, groups.uncompressed_size);
-  int relative_compressed_size =
-      (100 * groups.compressed_size) / groups.uncompressed_size;
-  base::UmaHistogramPercentage(
-      "Ads.InterestGroup.ServerAuction.Request.RelativeCompressedSize",
-      relative_compressed_size);
+  if (groups.uncompressed_size > 0) {
+    int relative_compressed_size =
+        (100 * groups.compressed_size) / groups.uncompressed_size;
+    base::UmaHistogramPercentage(
+        "Ads.InterestGroup.ServerAuction.Request.RelativeCompressedSize",
+        relative_compressed_size);
+  }
   base::UmaHistogramCounts1000(
       "Ads.InterestGroup.ServerAuction.Request.NumGroups", groups.num_groups);
 
@@ -949,25 +966,38 @@
   DCHECK(maybe_msg);
   DCHECK_EQ(static_cast<size_t>(total_size.ValueOrDie()), maybe_msg->size());
 
-  const size_t size_before_padding =
-      base::CheckAdd(framing_size, maybe_msg->size()).ValueOrDie();
-  uint32_t desired_size = absl::bit_ceil(size_before_padding);
+  base::CheckedNumeric<uint32_t> desired_size;
+  if (config->per_buyer_configs.empty()) {
+    // If we didn't set a list of buyers then use the requested size as the
+    // maximum size bucket.
+    const size_t size_before_padding =
+        base::CheckAdd(framing_size, maybe_msg->size()).ValueOrDie();
+    DCHECK_GE(config->request_size.value(), size_before_padding);
 
-  if (config_->request_size) {
-    DCHECK_GE(*config_->request_size, size_before_padding);
-    if (config_->per_buyer_configs.empty()) {
-      // If we didn't set a list of buyers then use the requested size as the
-      // maximum size bucket.
-      desired_size = std::min(desired_size, config_->request_size.value());
+    auto size_iter = std::lower_bound(kBinSizes.begin(), kBinSizes.end(),
+                                      size_before_padding);
+    if (size_iter != kBinSizes.end()) {
+      desired_size = std::min(*size_iter, config->request_size.value());
     } else {
-      // For customized requests we always use the requested size.
-      desired_size = config_->request_size.value();
+      desired_size = config->request_size.value();
     }
+  } else {
+    // For customized requests we *MUST* always use the requested size.
+    // Since the page can specify which buyers are included in the request, the
+    // request size could leak interest group state for a specific buyer if the
+    // size was allowed to vary.
+    desired_size = config->request_size.value();
   }
-  size_t padded_size = desired_size - framing_size + kFramingHeaderSize;
-  CHECK_GE(padded_size, maybe_msg->size() + kFramingHeaderSize);
+  base::CheckedNumeric<size_t> padded_size =
+      desired_size - framing_size + kFramingHeaderSize;
+  if (!padded_size.IsValid()) {
+    DLOG(ERROR) << "padded_size is invalid";
+    return {};
+  }
+  CHECK_GE(static_cast<size_t>(padded_size.ValueOrDie()),
+           maybe_msg->size() + kFramingHeaderSize);
 
-  std::vector<uint8_t> request(padded_size);
+  std::vector<uint8_t> request(padded_size.ValueOrDie());
   // first byte is version and compression
   request[0] = (kRequestVersion << kRequestVersionBitOffset) |
                (kGzipCompression << kCompressionBitOffset);
diff --git a/content/browser/interest_group/bidding_and_auction_serializer_unittest.cc b/content/browser/interest_group/bidding_and_auction_serializer_unittest.cc
index 121dcb6..63e619ab 100644
--- a/content/browser/interest_group/bidding_and_auction_serializer_unittest.cc
+++ b/content/browser/interest_group/bidding_and_auction_serializer_unittest.cc
@@ -103,9 +103,9 @@
   AddGroupsToSerializer(serializer);
 
   BiddingAndAuctionData data = serializer.Build();
-  EXPECT_EQ(data.request.size(), 4096u - kEncryptionOverhead);
+  EXPECT_EQ(data.request.size(), 5 * 1024 - kEncryptionOverhead);
   histogram_tester.ExpectTotalCount(
-      "Ads.InterestGroup.ServerAuction.Request.NumIterations", 0);
+      "Ads.InterestGroup.ServerAuction.Request.NumIterations", 4);
   histogram_tester.ExpectUniqueSample(
       "Ads.InterestGroup.ServerAuction.Request.NumGroups", 400, 1);
   histogram_tester.ExpectUniqueSample(
@@ -375,6 +375,61 @@
       "Ads.InterestGroup.ServerAuction.Request.RelativeCompressedSize", 1, 1);
 }
 
+// Test that the encrypted request still has the full size even when the
+// specified buyers are not on the device.
+TEST_F(BiddingAndAuctionSerializerTest, SerializeWithNoGroupsSetBuyersFixed) {
+  const size_t kRequestSize = 3000;
+  blink::mojom::AuctionDataConfigPtr config =
+      blink::mojom::AuctionDataConfig::New();
+  config->request_size = kRequestSize;
+
+  config->per_buyer_configs[kOriginA] =
+      blink::mojom::AuctionDataBuyerConfig::New(/*size=*/100);
+  config->per_buyer_configs[kOriginB] =
+      blink::mojom::AuctionDataBuyerConfig::New(/*size=*/100);
+  config->per_buyer_configs[kOriginC] =
+      blink::mojom::AuctionDataBuyerConfig::New(/*size=*/100);
+  config->per_buyer_configs[kOriginD] =
+      blink::mojom::AuctionDataBuyerConfig::New();
+
+  BiddingAndAuctionSerializer serializer;
+  serializer.SetPublisher("foo");
+  serializer.SetGenerationId(
+      base::Uuid::ParseCaseInsensitive("00000000-0000-0000-0000-000000000000"));
+  serializer.SetConfig(std::move(config));
+
+  BiddingAndAuctionData data = serializer.Build();
+  EXPECT_EQ(data.request.size(), kRequestSize - kEncryptionOverhead);
+}
+
+// Test that the encrypted request still has the full size even when the
+// specified buyers are not on the device.
+TEST_F(BiddingAndAuctionSerializerTest,
+       SerializeWithNoGroupsSetBuyersProportional) {
+  const size_t kRequestSize = 3000;
+  blink::mojom::AuctionDataConfigPtr config =
+      blink::mojom::AuctionDataConfig::New();
+  config->request_size = kRequestSize;
+
+  config->per_buyer_configs[kOriginA] =
+      blink::mojom::AuctionDataBuyerConfig::New(/*size=*/100);
+  config->per_buyer_configs[kOriginB] =
+      blink::mojom::AuctionDataBuyerConfig::New(/*size=*/100);
+  config->per_buyer_configs[kOriginC] =
+      blink::mojom::AuctionDataBuyerConfig::New(/*size=*/100);
+  config->per_buyer_configs[kOriginD] =
+      blink::mojom::AuctionDataBuyerConfig::New(/*size=*/100);
+
+  BiddingAndAuctionSerializer serializer;
+  serializer.SetPublisher("foo");
+  serializer.SetGenerationId(
+      base::Uuid::ParseCaseInsensitive("00000000-0000-0000-0000-000000000000"));
+  serializer.SetConfig(std::move(config));
+
+  BiddingAndAuctionData data = serializer.Build();
+  EXPECT_EQ(data.request.size(), kRequestSize - kEncryptionOverhead);
+}
+
 class TargetSizeEstimatorTest : public testing::Test {
  protected:
   const GURL kUrlA = GURL(kOriginStringA);