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