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