15d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov/*
25d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov * Copyright (C) 2016 The Android Open Source Project
35d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov *
45d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov * Licensed under the Apache License, Version 2.0 (the "License");
55d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov * you may not use this file except in compliance with the License.
65d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov * You may obtain a copy of the License at
75d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov *
85d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov *      http://www.apache.org/licenses/LICENSE-2.0
95d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov *
105d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov * Unless required by applicable law or agreed to in writing, software
115d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov * distributed under the License is distributed on an "AS IS" BASIS,
125d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
135d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov * See the License for the specific language governing permissions and
145d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov * limitations under the License.
155d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov */
165d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov
175d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganovpackage android.util;
185d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov
195d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganovimport android.annotation.NonNull;
205d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganovimport android.annotation.Nullable;
215d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganovimport android.content.pm.Signature;
225d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov
23cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganovimport java.io.ByteArrayOutputStream;
24cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganovimport java.io.IOException;
255d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganovimport java.security.MessageDigest;
265d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganovimport java.security.NoSuchAlgorithmException;
27cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganovimport java.util.Arrays;
285d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov
295d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov/**
305d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov * Helper functions applicable to packages.
315d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov * @hide
325d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov */
335d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganovpublic final class PackageUtils {
345d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov
355d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov    private PackageUtils() {
365d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov        /* hide constructor */
375d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov    }
385d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov
395d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov    /**
40cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov     * Computes the SHA256 digests of a list of signatures. Items in the
41cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov     * resulting array of hashes correspond to the signatures in the
42cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov     * input array.
43cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov     * @param signatures The signatures.
44cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov     * @return The digest array.
455d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov     */
46cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov    public static @NonNull String[] computeSignaturesSha256Digests(
47cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov            @NonNull Signature[] signatures) {
48cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov        final int signatureCount = signatures.length;
49cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov        final String[] digests = new String[signatureCount];
50cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov        for (int i = 0; i < signatureCount; i++) {
51cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov            digests[i] = computeSha256Digest(signatures[i].toByteArray());
52cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov        }
53cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov        return digests;
54cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov    }
55cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov    /**
56cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov     * Computes a SHA256 digest of the signatures' SHA256 digests. First,
57cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov     * individual hashes for each signature is derived in a hexademical
58cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov     * form, then these strings are sorted based the natural ordering, and
59cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov     * finally a hash is derived from these strings' bytes.
60cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov     * @param signatures The signatures.
61cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov     * @return The digest.
62cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov     */
63cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov    public static @NonNull String computeSignaturesSha256Digest(
64cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov            @NonNull Signature[] signatures) {
65cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov        // Shortcut for optimization - most apps singed by a single cert
66cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov        if (signatures.length == 1) {
67cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov            return computeSha256Digest(signatures[0].toByteArray());
685d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov        }
69cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov
70cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov        // Make sure these are sorted to handle reversed certificates
71cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov        final String[] sha256Digests = computeSignaturesSha256Digests(signatures);
72cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov        return computeSignaturesSha256Digest(sha256Digests);
735d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov    }
745d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov
755d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov    /**
76cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov     * Computes a SHA256 digest in of the signatures SHA256 digests. First,
77cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov     * the strings are sorted based the natural ordering, and then a hash is
78cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov     * derived from these strings' bytes.
79cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov     * @param sha256Digests Signature SHA256 hashes in hexademical form.
80cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov     * @return The digest.
815d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov     */
82cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov    public static @NonNull String computeSignaturesSha256Digest(
83cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov            @NonNull String[] sha256Digests) {
84cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov        // Shortcut for optimization - most apps singed by a single cert
85cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov        if (sha256Digests.length == 1) {
86cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov            return sha256Digests[0];
87cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov        }
88cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov
89cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov        // Make sure these are sorted to handle reversed certificates
90cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov        Arrays.sort(sha256Digests);
91cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov
92cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov        final ByteArrayOutputStream bytes = new ByteArrayOutputStream();
93cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov        for (String sha256Digest : sha256Digests) {
94cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov            try {
95cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov                bytes.write(sha256Digest.getBytes());
96cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov            } catch (IOException e) {
97cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov                /* ignore - can't happen */
98cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov            }
99cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov        }
100cdd685c07504223e37e7831ce592446ec4ac6f6aSvetoslav Ganov        return computeSha256Digest(bytes.toByteArray());
1015d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov    }
1025d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov
1035d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov    /**
1045d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov     * Computes the SHA256 digest of some data.
1055d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov     * @param data The data.
1065d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov     * @return The digest or null if an error occurs.
1075d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov     */
1085cdda3425ccf3c62e400a1646615f4479a8266afDaniel Cashman    public static @Nullable byte[] computeSha256DigestBytes(@NonNull byte[] data) {
1095d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov        MessageDigest messageDigest;
1105d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov        try {
1115d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov            messageDigest = MessageDigest.getInstance("SHA256");
1125d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov        } catch (NoSuchAlgorithmException e) {
1135d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov            /* can't happen */
1145d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov            return null;
1155d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov        }
1165d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov
1175d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov        messageDigest.update(data);
1185d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov
1195cdda3425ccf3c62e400a1646615f4479a8266afDaniel Cashman        return messageDigest.digest();
1205cdda3425ccf3c62e400a1646615f4479a8266afDaniel Cashman    }
1215cdda3425ccf3c62e400a1646615f4479a8266afDaniel Cashman
1225cdda3425ccf3c62e400a1646615f4479a8266afDaniel Cashman    /**
1235cdda3425ccf3c62e400a1646615f4479a8266afDaniel Cashman     * Computes the SHA256 digest of some data.
1245cdda3425ccf3c62e400a1646615f4479a8266afDaniel Cashman     * @param data The data.
1255cdda3425ccf3c62e400a1646615f4479a8266afDaniel Cashman     * @return The digest or null if an error occurs.
1265cdda3425ccf3c62e400a1646615f4479a8266afDaniel Cashman     */
1275cdda3425ccf3c62e400a1646615f4479a8266afDaniel Cashman    public static @Nullable String computeSha256Digest(@NonNull byte[] data) {
1285cdda3425ccf3c62e400a1646615f4479a8266afDaniel Cashman        return ByteStringUtils.toHexString(computeSha256DigestBytes(data));
1295d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov    }
1305d09c998a03eea53218c3b3c40e20db1b7693c9cSvet Ganov}
131