Home | History | Annotate | Download | only in backup
      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