1 /* 2 * Copyright (C) 2021 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.ext.services.displayhash; 18 19 import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_INVALID_HASH_ALGORITHM; 20 import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_UNKNOWN; 21 22 import android.graphics.Rect; 23 import android.hardware.HardwareBuffer; 24 import android.os.Build; 25 import android.service.displayhash.DisplayHashParams; 26 import android.service.displayhash.DisplayHashingService; 27 import android.util.ArrayMap; 28 import android.util.Log; 29 import android.view.displayhash.DisplayHash; 30 import android.view.displayhash.DisplayHashResultCallback; 31 import android.view.displayhash.VerifiedDisplayHash; 32 33 import androidx.annotation.NonNull; 34 import androidx.annotation.RequiresApi; 35 36 import com.google.common.annotations.VisibleForTesting; 37 38 import java.util.Map; 39 40 /** 41 * The implementation service for {@link DisplayHashingService} 42 */ 43 @RequiresApi(Build.VERSION_CODES.S) 44 public class DisplayHashingServiceImpl extends DisplayHashingService { 45 static final String TAG = "DisplayHashingService"; 46 47 private static final int INTERVAL_BETWEEN_REQUESTS_MILLIS = 10000; 48 49 private ImageHashManager mImageHashManager = new ImageHashManager(); 50 private final HmacKeyManager mHmacKeyManager = new HmacKeyManager(); 51 private final ArrayMap<String, DisplayHashParams> mDisplayHashParams = new ArrayMap<>(); 52 53 @Override onCreate()54 public void onCreate() { 55 super.onCreate(); 56 mDisplayHashParams.put("PHASH", new DisplayHashParams.Builder() 57 .setBufferSize(32, 32) 58 .setGrayscaleBuffer(true).build()); 59 } 60 61 @Override onGenerateDisplayHash(@onNull byte[] salt, @NonNull HardwareBuffer buffer, @NonNull Rect bounds, @NonNull String hashAlgorithm, @NonNull DisplayHashResultCallback callback)62 public void onGenerateDisplayHash(@NonNull byte[] salt, 63 @NonNull HardwareBuffer buffer, @NonNull Rect bounds, 64 @NonNull String hashAlgorithm, @NonNull DisplayHashResultCallback callback) { 65 if (salt == null) { 66 Log.w(TAG, "Failed to generate display hash: salt was null"); 67 callback.onDisplayHashError(DISPLAY_HASH_ERROR_UNKNOWN); 68 return; 69 } 70 71 int hashAlgorithmIndex = getIndexForHashAlgorithm(hashAlgorithm); 72 if (hashAlgorithmIndex < 0) { 73 Log.w(TAG, "Failed to generate display hash: invalid hash request"); 74 callback.onDisplayHashError(DISPLAY_HASH_ERROR_INVALID_HASH_ALGORITHM); 75 return; 76 } 77 78 if (buffer == null) { 79 Log.w(TAG, "Failed to generate display hash: null buffer"); 80 callback.onDisplayHashError(DISPLAY_HASH_ERROR_UNKNOWN); 81 return; 82 } 83 84 long timestamp = System.currentTimeMillis(); 85 byte[] imageHash = mImageHashManager.generateHash(buffer, hashAlgorithm); 86 if (imageHash == null) { 87 Log.w(TAG, "Failed to generate display hash: failed to create image hash"); 88 callback.onDisplayHashError(DISPLAY_HASH_ERROR_UNKNOWN); 89 return; 90 } 91 92 byte[] hmac = mHmacKeyManager.generateHmac(salt, timestamp, bounds, hashAlgorithmIndex, 93 imageHash); 94 callback.onDisplayHashResult( 95 new DisplayHash(timestamp, bounds, hashAlgorithm, imageHash, hmac)); 96 } 97 98 @Override onVerifyDisplayHash(@onNull byte[] salt, @NonNull DisplayHash displayHash)99 public VerifiedDisplayHash onVerifyDisplayHash(@NonNull byte[] salt, 100 @NonNull DisplayHash displayHash) { 101 if (displayHash == null || salt == null) { 102 Log.w(TAG, "Failed to verify display hash: invalid token or salt"); 103 return null; 104 } 105 106 boolean verified = mHmacKeyManager.verifyHmac(salt, 107 displayHash.getTimeMillis(), displayHash.getBoundsInWindow(), 108 getIndexForHashAlgorithm(displayHash.getHashAlgorithm()), 109 displayHash.getImageHash(), displayHash.getHmac()); 110 111 if (verified) { 112 return new VerifiedDisplayHash(displayHash.getTimeMillis(), 113 displayHash.getBoundsInWindow(), displayHash.getHashAlgorithm(), 114 displayHash.getImageHash()); 115 } else { 116 return null; 117 } 118 } 119 120 @Override onGetIntervalBetweenRequestsMillis()121 public int onGetIntervalBetweenRequestsMillis() { 122 return INTERVAL_BETWEEN_REQUESTS_MILLIS; 123 } 124 getIndexForHashAlgorithm(String hashAlgorithm)125 private int getIndexForHashAlgorithm(String hashAlgorithm) { 126 return mDisplayHashParams.indexOfKey(hashAlgorithm); 127 } 128 129 @VisibleForTesting setImageHashManager(ImageHashManager imageHashManager)130 public void setImageHashManager(ImageHashManager imageHashManager) { 131 mImageHashManager = imageHashManager; 132 } 133 134 @NonNull 135 @Override onGetDisplayHashAlgorithms()136 public Map<String, DisplayHashParams> onGetDisplayHashAlgorithms() { 137 return mDisplayHashParams; 138 } 139 } 140