1 /*
2  * Copyright (C) 2023 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 android.adservices.adselection;
18 
19 import static com.android.adservices.service.adselection.signature.ProtectedAudienceSignatureManager.PRIVATE_TEST_KEY_STRING;
20 
21 import android.adservices.common.AdData;
22 import android.adservices.common.AdDataFixture;
23 import android.adservices.common.AdTechIdentifier;
24 import android.adservices.common.CommonFixture;
25 import android.net.Uri;
26 
27 import com.android.adservices.LoggerFactory;
28 import com.android.adservices.service.adselection.signature.SignedContextualAdsHashUtil;
29 
30 import com.google.common.collect.ImmutableList;
31 import com.google.common.collect.ImmutableMap;
32 
33 import java.security.KeyFactory;
34 import java.security.PrivateKey;
35 import java.security.Signature;
36 import java.security.spec.PKCS8EncodedKeySpec;
37 import java.util.Base64;
38 import java.util.List;
39 import java.util.Map;
40 import java.util.stream.Collectors;
41 
42 /**
43  * This is a static class meant to help with tests that involve creating an {@link
44  * SignedContextualAds}.
45  */
46 public class SignedContextualAdsFixture {
47     private static final LoggerFactory.Logger sLogger = LoggerFactory.getFledgeLogger();
48     private static final byte[] PLACEHOLDER_EMPTY_SIGNATURE = new byte[] {};
49     public static final AdTechIdentifier BUYER = CommonFixture.VALID_BUYER_1;
50     public static final AdTechIdentifier BUYER_2 = CommonFixture.VALID_BUYER_2;
51 
52     // Uri Constants
53     public static final String DECISION_LOGIC_FRAGMENT = "/decisionFragment";
54 
55     public static final Uri DECISION_LOGIC_URI =
56             CommonFixture.getUri(BUYER, DECISION_LOGIC_FRAGMENT);
57 
58     private static final AdData VALID_AD_DATA = AdDataFixture.getValidAdDataByBuyer(BUYER, 0);
59     private static final double TEST_BID = 0.1;
60 
61     public static final AdWithBid AD_WITH_BID_1 = new AdWithBid(VALID_AD_DATA, TEST_BID);
62     public static final AdWithBid AD_WITH_BID_2 = new AdWithBid(VALID_AD_DATA, TEST_BID * 2);
63     public static final List<AdWithBid> ADS_WITH_BID =
64             ImmutableList.of(AD_WITH_BID_1, AD_WITH_BID_2);
65 
66     /**
67      * Returns a {@link SignedContextualAds} object with a placeholder signature
68      *
69      * <p>This object's signature will not pass the verification
70      */
aContextualAdsWithEmptySignatureBuilder()71     public static SignedContextualAds.Builder aContextualAdsWithEmptySignatureBuilder() {
72         return new SignedContextualAds.Builder()
73                 .setBuyer(BUYER)
74                 .setDecisionLogicUri(DECISION_LOGIC_URI)
75                 .setAdsWithBid(ADS_WITH_BID)
76                 .setSignature(PLACEHOLDER_EMPTY_SIGNATURE);
77     }
78 
79     /**
80      * Returns a {@link SignedContextualAds} object with a placeholder signature and the given
81      * buyer.
82      *
83      * <p>This object's signature will not pass the verification
84      */
aContextualAdsWithEmptySignatureBuilder( AdTechIdentifier buyer)85     public static SignedContextualAds.Builder aContextualAdsWithEmptySignatureBuilder(
86             AdTechIdentifier buyer) {
87         return aContextualAdsWithEmptySignatureBuilder()
88                 .setBuyer(buyer)
89                 .setDecisionLogicUri(CommonFixture.getUri(buyer, DECISION_LOGIC_FRAGMENT));
90     }
91 
92     /**
93      * Returns a {@link SignedContextualAds} object with a placeholder signature with given buyer
94      * and bids.
95      *
96      * <p>This object's signature will not pass the verification
97      */
aContextualAdsWithEmptySignatureBuilder( AdTechIdentifier buyer, List<Double> bids)98     public static SignedContextualAds.Builder aContextualAdsWithEmptySignatureBuilder(
99             AdTechIdentifier buyer, List<Double> bids) {
100         List<AdWithBid> adsWithBid =
101                 bids.stream()
102                         .map(
103                                 bid ->
104                                         new AdWithBid(
105                                                 AdDataFixture.getValidFilterAdDataByBuyer(
106                                                         buyer, bid.intValue()),
107                                                 bid))
108                         .collect(Collectors.toList());
109         return aContextualAdsWithEmptySignatureBuilder(buyer).setAdsWithBid(adsWithBid);
110     }
111 
112     /**
113      * Returns a {@link SignedContextualAds} object that is signed.
114      *
115      * <p>This object's signature can be verified using {@link SignedContextualAdsFixture
116      * #PUBLIC_KEY_STRING}.
117      */
aSignedContextualAds()118     public static SignedContextualAds aSignedContextualAds() {
119         return signContextualAds(aContextualAdsWithEmptySignatureBuilder());
120     }
121 
122     /**
123      * Returns a {@link SignedContextualAds} object that is signed with given buyer.
124      *
125      * <p>This object's signature can be verified using {@link SignedContextualAdsFixture
126      * #PUBLIC_KEY_STRING}.
127      */
aSignedContextualAds(AdTechIdentifier buyer)128     public static SignedContextualAds aSignedContextualAds(AdTechIdentifier buyer) {
129         return signContextualAds(aContextualAdsWithEmptySignatureBuilder(buyer));
130     }
131 
132     /**
133      * Returns a {@link SignedContextualAds} object that is signed with given buyer.
134      *
135      * <p>This object's signature can be verified using {@link SignedContextualAdsFixture
136      * #PUBLIC_KEY_STRING}.
137      */
aSignedContextualAds( AdTechIdentifier buyer, List<Double> bids)138     public static SignedContextualAds aSignedContextualAds(
139             AdTechIdentifier buyer, List<Double> bids) {
140         return signContextualAds(aContextualAdsWithEmptySignatureBuilder(buyer, bids));
141     }
142 
143     public static ImmutableMap<AdTechIdentifier, SignedContextualAds>
getBuyerSignedContextualAdsMap()144             getBuyerSignedContextualAdsMap() {
145         return ImmutableMap.of(
146                 CommonFixture.VALID_BUYER_1,
147                 aSignedContextualAds(CommonFixture.VALID_BUYER_1),
148                 CommonFixture.VALID_BUYER_2,
149                 aSignedContextualAds(CommonFixture.VALID_BUYER_2));
150     }
151 
152     /**
153      * Signs contextual ads using {@link
154      * com.android.adservices.service.adselection.signature.ProtectedAudienceSignatureManager
155      * #PRIVATE_KEY_STRING}.
156      *
157      * <p>Bundle can be verified using {@link
158      * com.android.adservices.service.adselection.signature.ProtectedAudienceSignatureManager
159      * #PUBLIC_KEY_STRING}
160      */
signContextualAds( SignedContextualAds.Builder notSignedContextualAdsBuilder)161     public static SignedContextualAds signContextualAds(
162             SignedContextualAds.Builder notSignedContextualAdsBuilder) {
163         SignedContextualAds signedContextualAds;
164         try {
165             Signature ecdsaSigner = getECDSASignatureInstance();
166             ecdsaSigner.update(
167                     new SignedContextualAdsHashUtil()
168                             .serialize(notSignedContextualAdsBuilder.build()));
169             signedContextualAds =
170                     notSignedContextualAdsBuilder.setSignature(ecdsaSigner.sign()).build();
171         } catch (Exception e) {
172             String errMsg =
173                     String.format(
174                             "Something went wrong during signing a contextual ad bundle: %s", e);
175             sLogger.v(errMsg);
176             throw new RuntimeException(errMsg, e);
177         }
178         return signedContextualAds;
179     }
180 
getECDSASignatureInstance()181     private static Signature getECDSASignatureInstance() throws Exception {
182         byte[] privateKeyBytes = Base64.getDecoder().decode(PRIVATE_TEST_KEY_STRING);
183         PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(privateKeyBytes);
184         KeyFactory keyFactory = KeyFactory.getInstance("EC");
185         PrivateKey privateKey = keyFactory.generatePrivate(spec);
186         Signature ecdsaSign = Signature.getInstance("SHA256withECDSA");
187         ecdsaSign.initSign(privateKey);
188         return ecdsaSign;
189     }
190 
191     /**
192      * Returns the number of ads inside a {@link android.adservices.adselection.SignedContextualAds}
193      * bundle
194      */
countAdsIn(Map<?, SignedContextualAds> signedContextualAdsMap)195     public static int countAdsIn(Map<?, SignedContextualAds> signedContextualAdsMap) {
196         return signedContextualAdsMap.values().stream()
197                 .mapToInt(contextualAds -> contextualAds.getAdsWithBid().size())
198                 .sum();
199     }
200 }
201