1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.adservices.service.adselection.encryption; 18 19 import static com.android.adservices.service.common.httpclient.AdServicesHttpUtil.REQUEST_PROPERTIES_PROTOBUF_CONTENT_TYPE; 20 import static com.android.adservices.service.common.httpclient.AdServicesHttpUtil.RESPONSE_PROPERTIES_CONTENT_TYPE; 21 import static com.android.adservices.service.stats.AdsRelevanceStatusUtils.SERVER_AUCTION_COORDINATOR_SOURCE_API; 22 import static com.android.adservices.service.stats.AdsRelevanceStatusUtils.SERVER_AUCTION_COORDINATOR_SOURCE_DEFAULT; 23 import static com.android.adservices.service.stats.AdsRelevanceStatusUtils.SERVER_AUCTION_ENCRYPTION_KEY_SOURCE_NETWORK; 24 25 import android.net.Uri; 26 27 import androidx.annotation.NonNull; 28 import androidx.annotation.Nullable; 29 30 import com.android.adservices.LoggerFactory; 31 import com.android.adservices.data.adselection.DBEncryptionKey; 32 import com.android.adservices.data.adselection.EncryptionKeyConstants; 33 import com.android.adservices.ohttp.ObliviousHttpKeyConfig; 34 import com.android.adservices.service.Flags; 35 import com.android.adservices.service.common.AllowLists; 36 import com.android.adservices.service.common.httpclient.AdServicesHttpClientRequest; 37 import com.android.adservices.service.common.httpclient.AdServicesHttpClientResponse; 38 import com.android.adservices.service.common.httpclient.AdServicesHttpsClient; 39 import com.android.adservices.service.devapi.DevContext; 40 import com.android.adservices.service.stats.AdServicesLogger; 41 import com.android.adservices.service.stats.FetchProcessLogger; 42 43 import com.google.common.collect.ImmutableList; 44 import com.google.common.util.concurrent.FluentFuture; 45 import com.google.common.util.concurrent.ListenableFuture; 46 47 import java.security.spec.InvalidKeySpecException; 48 import java.time.Clock; 49 import java.time.Instant; 50 import java.util.List; 51 import java.util.Objects; 52 import java.util.Set; 53 import java.util.concurrent.ExecutorService; 54 55 public abstract class ProtectedServersEncryptionConfigManagerBase { 56 protected static final LoggerFactory.Logger sLogger = LoggerFactory.getFledgeLogger(); 57 protected final Clock mClock; 58 protected final ExecutorService mLightweightExecutor; 59 60 protected final Flags mFlags; 61 protected final AdServicesLogger mAdServicesLogger; 62 63 protected final AuctionEncryptionKeyParser mAuctionEncryptionKeyParser; 64 protected final JoinEncryptionKeyParser mJoinEncryptionKeyParser; 65 protected final AdServicesHttpsClient mAdServicesHttpsClient; 66 67 @Nullable getLatestOhttpKeyConfigOfType( @dSelectionEncryptionKey.AdSelectionEncryptionKeyType int adSelectionEncryptionKeyType, long timeoutMs, @Nullable Uri coordinatorUrl, DevContext devContext)68 abstract FluentFuture<ObliviousHttpKeyConfig> getLatestOhttpKeyConfigOfType( 69 @AdSelectionEncryptionKey.AdSelectionEncryptionKeyType int adSelectionEncryptionKeyType, 70 long timeoutMs, 71 @Nullable Uri coordinatorUrl, 72 DevContext devContext); 73 fetchAndPersistActiveKeysOfType( @dSelectionEncryptionKey.AdSelectionEncryptionKeyType int adSelectionKeyType, Instant keyExpiryInstant, long timeoutMs, @Nullable Uri coordinatorUrl, DevContext devContext, FetchProcessLogger keyFetchLogger)74 abstract FluentFuture<List<DBEncryptionKey>> fetchAndPersistActiveKeysOfType( 75 @AdSelectionEncryptionKey.AdSelectionEncryptionKeyType int adSelectionKeyType, 76 Instant keyExpiryInstant, 77 long timeoutMs, 78 @Nullable Uri coordinatorUrl, 79 DevContext devContext, 80 FetchProcessLogger keyFetchLogger); 81 getExpiredAdSelectionEncryptionKeyTypes(Instant keyExpiryInstant)82 abstract Set<Integer> getExpiredAdSelectionEncryptionKeyTypes(Instant keyExpiryInstant); 83 getAbsentAdSelectionEncryptionKeyTypes()84 abstract Set<Integer> getAbsentAdSelectionEncryptionKeyTypes(); 85 ProtectedServersEncryptionConfigManagerBase( @onNull Flags flags, @NonNull Clock clock, @NonNull AuctionEncryptionKeyParser auctionEncryptionKeyParser, @NonNull JoinEncryptionKeyParser joinEncryptionKeyParser, @NonNull AdServicesHttpsClient adServicesHttpsClient, @NonNull ExecutorService lightweightExecutor, @NonNull AdServicesLogger adServicesLogger)86 protected ProtectedServersEncryptionConfigManagerBase( 87 @NonNull Flags flags, 88 @NonNull Clock clock, 89 @NonNull AuctionEncryptionKeyParser auctionEncryptionKeyParser, 90 @NonNull JoinEncryptionKeyParser joinEncryptionKeyParser, 91 @NonNull AdServicesHttpsClient adServicesHttpsClient, 92 @NonNull ExecutorService lightweightExecutor, 93 @NonNull AdServicesLogger adServicesLogger) { 94 Objects.requireNonNull(flags); 95 Objects.requireNonNull(clock); 96 Objects.requireNonNull(auctionEncryptionKeyParser); 97 Objects.requireNonNull(joinEncryptionKeyParser); 98 Objects.requireNonNull(adServicesHttpsClient); 99 Objects.requireNonNull(lightweightExecutor); 100 Objects.requireNonNull(adServicesLogger); 101 102 this.mFlags = flags; 103 this.mClock = clock; 104 this.mAuctionEncryptionKeyParser = auctionEncryptionKeyParser; 105 this.mJoinEncryptionKeyParser = joinEncryptionKeyParser; 106 this.mAdServicesHttpsClient = adServicesHttpsClient; 107 this.mLightweightExecutor = lightweightExecutor; 108 this.mAdServicesLogger = adServicesLogger; 109 } 110 parseKeyResponse( AdServicesHttpClientResponse keyFetchResponse, @AdSelectionEncryptionKey.AdSelectionEncryptionKeyType int encryptionKeyType)111 protected List<DBEncryptionKey> parseKeyResponse( 112 AdServicesHttpClientResponse keyFetchResponse, 113 @AdSelectionEncryptionKey.AdSelectionEncryptionKeyType int encryptionKeyType) { 114 switch (encryptionKeyType) { 115 case AdSelectionEncryptionKey.AdSelectionEncryptionKeyType.AUCTION: 116 return mAuctionEncryptionKeyParser.getDbEncryptionKeys(keyFetchResponse); 117 case AdSelectionEncryptionKey.AdSelectionEncryptionKeyType.JOIN: 118 return mJoinEncryptionKeyParser.getDbEncryptionKeys(keyFetchResponse); 119 case AdSelectionEncryptionKey.AdSelectionEncryptionKeyType.UNASSIGNED: 120 default: 121 return ImmutableList.of(); 122 } 123 } 124 parseDbEncryptionKey(DBEncryptionKey dbEncryptionKey)125 protected AdSelectionEncryptionKey parseDbEncryptionKey(DBEncryptionKey dbEncryptionKey) { 126 switch (dbEncryptionKey.getEncryptionKeyType()) { 127 case EncryptionKeyConstants.EncryptionKeyType.ENCRYPTION_KEY_TYPE_AUCTION: 128 return mAuctionEncryptionKeyParser.parseDbEncryptionKey(dbEncryptionKey); 129 case EncryptionKeyConstants.EncryptionKeyType.ENCRYPTION_KEY_TYPE_JOIN: 130 return mJoinEncryptionKeyParser.parseDbEncryptionKey(dbEncryptionKey); 131 case EncryptionKeyConstants.EncryptionKeyType.ENCRYPTION_KEY_TYPE_QUERY: 132 default: 133 return null; 134 } 135 } 136 getKeyCountForType( @dSelectionEncryptionKey.AdSelectionEncryptionKeyType int type)137 protected int getKeyCountForType( 138 @AdSelectionEncryptionKey.AdSelectionEncryptionKeyType int type) { 139 switch (type) { 140 case AdSelectionEncryptionKey.AdSelectionEncryptionKeyType.AUCTION: 141 // For auctions, more than one key is fetched from the DB to mitigate impact 142 // due to key leakage. 143 return mFlags.getFledgeAuctionServerAuctionKeySharding(); 144 case AdSelectionEncryptionKey.AdSelectionEncryptionKeyType.JOIN: 145 return 1; 146 case AdSelectionEncryptionKey.AdSelectionEncryptionKeyType.UNASSIGNED: 147 default: 148 return 0; 149 } 150 } 151 getOhttpKeyConfigForKey(AdSelectionEncryptionKey encryptionKey)152 protected ObliviousHttpKeyConfig getOhttpKeyConfigForKey(AdSelectionEncryptionKey encryptionKey) 153 throws InvalidKeySpecException { 154 Objects.requireNonNull(encryptionKey); 155 switch (encryptionKey.keyType()) { 156 case AdSelectionEncryptionKey.AdSelectionEncryptionKeyType.AUCTION: 157 return mAuctionEncryptionKeyParser.getObliviousHttpKeyConfig(encryptionKey); 158 case AdSelectionEncryptionKey.AdSelectionEncryptionKeyType.JOIN: 159 return mJoinEncryptionKeyParser.getObliviousHttpKeyConfig(encryptionKey); 160 case AdSelectionEncryptionKey.AdSelectionEncryptionKeyType.UNASSIGNED: 161 default: 162 throw new IllegalArgumentException( 163 "Encryption Key of given type is not supported."); 164 } 165 } 166 fetchKeyPayload( @dSelectionEncryptionKey.AdSelectionEncryptionKeyType int adSelectionKeyType, Uri fetchUri, DevContext devContext, FetchProcessLogger keyFetchLogger)167 protected ListenableFuture<AdServicesHttpClientResponse> fetchKeyPayload( 168 @AdSelectionEncryptionKey.AdSelectionEncryptionKeyType int adSelectionKeyType, 169 Uri fetchUri, 170 DevContext devContext, 171 FetchProcessLogger keyFetchLogger) { 172 keyFetchLogger.setEncryptionKeySource(SERVER_AUCTION_ENCRYPTION_KEY_SOURCE_NETWORK); 173 switch (adSelectionKeyType) { 174 case AdSelectionEncryptionKey.AdSelectionEncryptionKeyType.AUCTION: 175 return mAdServicesHttpsClient.fetchPayloadWithLogging( 176 fetchUri, devContext, keyFetchLogger); 177 case AdSelectionEncryptionKey.AdSelectionEncryptionKeyType.JOIN: 178 AdServicesHttpClientRequest fetchKeyRequest = 179 AdServicesHttpClientRequest.builder() 180 .setUri(fetchUri) 181 .setRequestProperties(REQUEST_PROPERTIES_PROTOBUF_CONTENT_TYPE) 182 .setResponseHeaderKeys(RESPONSE_PROPERTIES_CONTENT_TYPE) 183 .setDevContext(devContext) 184 .build(); 185 return mAdServicesHttpsClient.performRequestGetResponseInBase64StringWithLogging( 186 fetchKeyRequest, keyFetchLogger); 187 case AdSelectionEncryptionKey.AdSelectionEncryptionKeyType.UNASSIGNED: 188 default: 189 throw new IllegalStateException( 190 "AdSelectionEncryptionKeyType: " 191 + adSelectionKeyType 192 + " is not supported."); 193 } 194 } 195 getKeyFetchUriOfType( @dSelectionEncryptionKey.AdSelectionEncryptionKeyType int adSelectionEncryptionKeyType, @Nullable Uri coordinatorUrl, @Nullable String allowList, FetchProcessLogger keyFetchLogger)196 protected Uri getKeyFetchUriOfType( 197 @AdSelectionEncryptionKey.AdSelectionEncryptionKeyType int adSelectionEncryptionKeyType, 198 @Nullable Uri coordinatorUrl, 199 @Nullable String allowList, 200 FetchProcessLogger keyFetchLogger) { 201 202 if (coordinatorUrl != null 203 && allowList != null 204 && adSelectionEncryptionKeyType 205 == AdSelectionEncryptionKey.AdSelectionEncryptionKeyType.AUCTION) { 206 keyFetchLogger.setCoordinatorSource(SERVER_AUCTION_COORDINATOR_SOURCE_API); 207 return getUriFromAllowlist(coordinatorUrl, allowList); 208 } 209 210 sLogger.v("The passed coordinatorUrl was null. Fetching default coordinator"); 211 keyFetchLogger.setCoordinatorSource(SERVER_AUCTION_COORDINATOR_SOURCE_DEFAULT); 212 213 switch (adSelectionEncryptionKeyType) { 214 case AdSelectionEncryptionKey.AdSelectionEncryptionKeyType.AUCTION: 215 return Uri.parse(mFlags.getFledgeAuctionServerAuctionKeyFetchUri()); 216 case AdSelectionEncryptionKey.AdSelectionEncryptionKeyType.JOIN: 217 return Uri.parse(mFlags.getFledgeAuctionServerJoinKeyFetchUri()); 218 case AdSelectionEncryptionKey.AdSelectionEncryptionKeyType.UNASSIGNED: 219 default: 220 return null; 221 } 222 } 223 getUriFromAllowlist(@ullable Uri coordinatorUrl, String allowlist)224 private Uri getUriFromAllowlist(@Nullable Uri coordinatorUrl, String allowlist) { 225 List<String> allowedUrls = AllowLists.splitAllowList(allowlist); 226 227 for (String url : allowedUrls) { 228 Uri allowedUri = Uri.parse(url); 229 if (coordinatorUrl.getHost().equals(allowedUri.getHost())) { 230 return allowedUri; 231 } 232 } 233 234 return null; 235 } 236 } 237