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 com.android.server.appsearch.util;
18 
19 import android.content.Context;
20 import android.content.pm.PackageInfo;
21 import android.content.pm.PackageManager;
22 import android.content.pm.PackageManager.NameNotFoundException;
23 import android.content.pm.Signature;
24 import android.os.Build;
25 
26 import java.security.MessageDigest;
27 import java.security.NoSuchAlgorithmException;
28 import java.util.Arrays;
29 
30 /**
31  * Utility to verify if the package is signed with correct certificates.
32  *
33  * @hide
34  */
35 public final class PackageManagerUtil {
36 
37     /**
38      * Verifies if the callingPackage has correct matching certificate.
39      *
40      * <p>For Pre-P devices, this matches with a single byte-array corresponding to the oldest
41      * available signature. For P+ devices, it used existing PackageManager's hasSigningCertificate
42      * implementation that takes rotation history in account.
43      *
44      * @param context Context of the calling app.
45      * @param packageName package whose signing certificates to check
46      * @param sha256cert sha256 of the signing certificate for which to search
47      * @return true if this package was or is signed by exactly the certificate with SHA-256 as
48      *     {@code sha256cert}
49      */
hasSigningCertificate( Context context, String packageName, byte[] sha256cert)50     public static boolean hasSigningCertificate(
51             Context context, String packageName, byte[] sha256cert) {
52         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
53             return hasSigningCertificateBelowP(context, packageName, sha256cert);
54         }
55 
56         return context.getPackageManager()
57                 .hasSigningCertificate(packageName, sha256cert, PackageManager.CERT_INPUT_SHA256);
58     }
59 
hasSigningCertificateBelowP( Context context, String packageName, byte[] sha256cert)60     private static boolean hasSigningCertificateBelowP(
61             Context context, String packageName, byte[] sha256cert) {
62         PackageInfo packageInfo;
63         try {
64             packageInfo =
65                     context.getPackageManager()
66                             .getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
67         } catch (NameNotFoundException e) {
68             throw new IllegalArgumentException("Given package does not exist on device!");
69         }
70         if (packageInfo == null) {
71             return false;
72         }
73 
74         // Verification of an android application requires set-equals matching, to avoid known
75         // security vulnerabilities a certificate hash will only be matched when exactly one
76         // certificate is present. See http://issuetracker.google.com/36992561 for more information.
77         try {
78             Signature[] signatures = packageInfo.signatures;
79             if (signatures != null && signatures.length == 1) {
80                 byte[] certificate =
81                         MessageDigest.getInstance(/* algorithm= */ "SHA-256")
82                                 .digest(signatures[0].toByteArray());
83                 return Arrays.equals(certificate, sha256cert);
84             }
85         } catch (NoSuchAlgorithmException e) {
86             throw new IllegalArgumentException("Provided SHA-256 implementation is invalid!");
87         }
88         return false;
89     }
90 
PackageManagerUtil()91     private PackageManagerUtil() {}
92 }
93