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