Home | History | Annotate | Download | only in util
      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 android.util;
     18 
     19 import android.annotation.NonNull;
     20 import android.annotation.Nullable;
     21 import android.content.pm.Signature;
     22 
     23 import java.io.ByteArrayOutputStream;
     24 import java.io.IOException;
     25 import java.security.MessageDigest;
     26 import java.security.NoSuchAlgorithmException;
     27 import java.util.Arrays;
     28 
     29 /**
     30  * Helper functions applicable to packages.
     31  * @hide
     32  */
     33 public final class PackageUtils {
     34 
     35     private PackageUtils() {
     36         /* hide constructor */
     37     }
     38 
     39     /**
     40      * Computes the SHA256 digests of a list of signatures. Items in the
     41      * resulting array of hashes correspond to the signatures in the
     42      * input array.
     43      * @param signatures The signatures.
     44      * @return The digest array.
     45      */
     46     public static @NonNull String[] computeSignaturesSha256Digests(
     47             @NonNull Signature[] signatures) {
     48         final int signatureCount = signatures.length;
     49         final String[] digests = new String[signatureCount];
     50         for (int i = 0; i < signatureCount; i++) {
     51             digests[i] = computeSha256Digest(signatures[i].toByteArray());
     52         }
     53         return digests;
     54     }
     55     /**
     56      * Computes a SHA256 digest of the signatures' SHA256 digests. First,
     57      * individual hashes for each signature is derived in a hexademical
     58      * form, then these strings are sorted based the natural ordering, and
     59      * finally a hash is derived from these strings' bytes.
     60      * @param signatures The signatures.
     61      * @return The digest.
     62      */
     63     public static @NonNull String computeSignaturesSha256Digest(
     64             @NonNull Signature[] signatures) {
     65         // Shortcut for optimization - most apps singed by a single cert
     66         if (signatures.length == 1) {
     67             return computeSha256Digest(signatures[0].toByteArray());
     68         }
     69 
     70         // Make sure these are sorted to handle reversed certificates
     71         final String[] sha256Digests = computeSignaturesSha256Digests(signatures);
     72         return computeSignaturesSha256Digest(sha256Digests);
     73     }
     74 
     75     /**
     76      * Computes a SHA256 digest in of the signatures SHA256 digests. First,
     77      * the strings are sorted based the natural ordering, and then a hash is
     78      * derived from these strings' bytes.
     79      * @param sha256Digests Signature SHA256 hashes in hexademical form.
     80      * @return The digest.
     81      */
     82     public static @NonNull String computeSignaturesSha256Digest(
     83             @NonNull String[] sha256Digests) {
     84         // Shortcut for optimization - most apps singed by a single cert
     85         if (sha256Digests.length == 1) {
     86             return sha256Digests[0];
     87         }
     88 
     89         // Make sure these are sorted to handle reversed certificates
     90         Arrays.sort(sha256Digests);
     91 
     92         final ByteArrayOutputStream bytes = new ByteArrayOutputStream();
     93         for (String sha256Digest : sha256Digests) {
     94             try {
     95                 bytes.write(sha256Digest.getBytes());
     96             } catch (IOException e) {
     97                 /* ignore - can't happen */
     98             }
     99         }
    100         return computeSha256Digest(bytes.toByteArray());
    101     }
    102 
    103     /**
    104      * Computes the SHA256 digest of some data.
    105      * @param data The data.
    106      * @return The digest or null if an error occurs.
    107      */
    108     public static @Nullable byte[] computeSha256DigestBytes(@NonNull byte[] data) {
    109         MessageDigest messageDigest;
    110         try {
    111             messageDigest = MessageDigest.getInstance("SHA256");
    112         } catch (NoSuchAlgorithmException e) {
    113             /* can't happen */
    114             return null;
    115         }
    116 
    117         messageDigest.update(data);
    118 
    119         return messageDigest.digest();
    120     }
    121 
    122     /**
    123      * Computes the SHA256 digest of some data.
    124      * @param data The data.
    125      * @return The digest or null if an error occurs.
    126      */
    127     public static @Nullable String computeSha256Digest(@NonNull byte[] data) {
    128         return ByteStringUtils.toHexString(computeSha256DigestBytes(data));
    129     }
    130 }
    131