blob: d817534551f7b331aae44ec81999cfe6a9107e29 [file] [log] [blame]
Makoto Onuki590096a2016-03-25 17:15:30 -07001/*
2 * Copyright (C) 2016 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
17package com.android.server.backup;
18
19import android.content.pm.ApplicationInfo;
20import android.content.pm.PackageInfo;
Michal Karpinski528c3e52018-02-07 17:47:10 +000021import android.content.pm.PackageManagerInternal;
Makoto Onuki590096a2016-03-25 17:15:30 -070022import android.content.pm.Signature;
23import android.util.Slog;
24
Michal Karpinski528c3e52018-02-07 17:47:10 +000025import com.android.internal.util.ArrayUtils;
26
Makoto Onuki590096a2016-03-25 17:15:30 -070027import java.security.MessageDigest;
28import java.security.NoSuchAlgorithmException;
29import java.util.ArrayList;
30import java.util.Arrays;
31import java.util.List;
32
33public class BackupUtils {
34 private static final String TAG = "BackupUtils";
35
Michal Karpinski528c3e52018-02-07 17:47:10 +000036 private static final boolean DEBUG = false;
Makoto Onuki590096a2016-03-25 17:15:30 -070037
Michal Karpinski528c3e52018-02-07 17:47:10 +000038 public static boolean signaturesMatch(ArrayList<byte[]> storedSigHashes, PackageInfo target,
39 PackageManagerInternal pmi) {
40 if (target == null || target.packageName == null) {
Makoto Onuki590096a2016-03-25 17:15:30 -070041 return false;
42 }
Makoto Onuki590096a2016-03-25 17:15:30 -070043 // If the target resides on the system partition, we allow it to restore
44 // data from the like-named package in a restore set even if the signatures
45 // do not match. (Unlike general applications, those flashed to the system
46 // partition will be signed with the device's platform certificate, so on
47 // different phones the same system app will have different signatures.)
48 if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
49 if (DEBUG) Slog.v(TAG, "System app " + target.packageName + " - skipping sig check");
50 return true;
51 }
52
Michal Karpinski528c3e52018-02-07 17:47:10 +000053 // Don't allow unsigned apps on either end
54 if (ArrayUtils.isEmpty(storedSigHashes)) {
Amith Yamasani14c716c2018-03-05 20:39:04 +000055 return false;
56 }
57
Michal Karpinski528c3e52018-02-07 17:47:10 +000058 Signature[][] deviceHistorySigs = target.signingCertificateHistory;
59 if (ArrayUtils.isEmpty(deviceHistorySigs)) {
60 Slog.w(TAG, "signingCertificateHistory is empty, app was either unsigned or the flag" +
61 " PackageManager#GET_SIGNING_CERTIFICATES was not specified");
62 return false;
Amith Yamasani14c716c2018-03-05 20:39:04 +000063 }
64
Michal Karpinski528c3e52018-02-07 17:47:10 +000065 if (DEBUG) {
66 Slog.v(TAG, "signaturesMatch(): stored=" + storedSigHashes
67 + " device=" + deviceHistorySigs);
68 }
69
70 final int nStored = storedSigHashes.size();
71 if (nStored == 1) {
72 // if the app is only signed with one sig, it's possible it has rotated its key
73 // the checks with signing history are delegated to PackageManager
74 // TODO(b/73988180): address the case that app has declared restoreAnyVersion and is
75 // restoring from higher version to lower after having rotated the key (i.e. higher
76 // version has different sig than lower version that we want to restore to)
77 return pmi.isDataRestoreSafe(storedSigHashes.get(0), target.packageName);
78 } else {
79 // the app couldn't have rotated keys, since it was signed with multiple sigs - do
80 // a check to see if we find a match for all stored sigs
81 // since app hasn't rotated key, we only need to check with deviceHistorySigs[0]
82 ArrayList<byte[]> deviceHashes = hashSignatureArray(deviceHistorySigs[0]);
83 int nDevice = deviceHashes.size();
84 // ensure that each stored sig matches an on-device sig
85 for (int i = 0; i < nStored; i++) {
86 boolean match = false;
87 for (int j = 0; j < nDevice; j++) {
88 if (Arrays.equals(storedSigHashes.get(i), deviceHashes.get(j))) {
89 match = true;
90 break;
91 }
92 }
93 if (!match) {
94 return false;
Amith Yamasani14c716c2018-03-05 20:39:04 +000095 }
96 }
Michal Karpinski528c3e52018-02-07 17:47:10 +000097 // we have found a match for all stored sigs
98 return true;
Amith Yamasani14c716c2018-03-05 20:39:04 +000099 }
Makoto Onuki590096a2016-03-25 17:15:30 -0700100 }
101
102 public static byte[] hashSignature(byte[] signature) {
103 try {
104 MessageDigest digest = MessageDigest.getInstance("SHA-256");
105 digest.update(signature);
106 return digest.digest();
107 } catch (NoSuchAlgorithmException e) {
108 Slog.w(TAG, "No SHA-256 algorithm found!");
109 }
110 return null;
111 }
112
113 public static byte[] hashSignature(Signature signature) {
114 return hashSignature(signature.toByteArray());
115 }
116
117 public static ArrayList<byte[]> hashSignatureArray(Signature[] sigs) {
118 if (sigs == null) {
119 return null;
120 }
121
122 ArrayList<byte[]> hashes = new ArrayList<>(sigs.length);
123 for (Signature s : sigs) {
124 hashes.add(hashSignature(s));
125 }
126 return hashes;
127 }
128
129 public static ArrayList<byte[]> hashSignatureArray(List<byte[]> sigs) {
130 if (sigs == null) {
131 return null;
132 }
133
134 ArrayList<byte[]> hashes = new ArrayList<>(sigs.size());
135 for (byte[] s : sigs) {
136 hashes.add(hashSignature(s));
137 }
138 return hashes;
139 }
140}