16a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen/*
26a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen * Copyright (C) 2015 The Android Open Source Project
36a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen *
46a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen * Licensed under the Apache License, Version 2.0 (the "License");
56a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen * you may not use this file except in compliance with the License.
66a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen * You may obtain a copy of the License at
76a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen *
86a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen *      http://www.apache.org/licenses/LICENSE-2.0
96a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen *
106a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen * Unless required by applicable law or agreed to in writing, software
116a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen * distributed under the License is distributed on an "AS IS" BASIS,
126a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen * See the License for the specific language governing permissions and
146a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen * limitations under the License.
156a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen */
166a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wenpackage com.android.statementservice.retriever;
176a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen
186a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wenimport android.content.Context;
196a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wenimport android.content.pm.PackageManager;
206a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wenimport android.content.pm.PackageManager.NameNotFoundException;
216a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wenimport android.content.pm.Signature;
226a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen
236a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wenimport java.security.MessageDigest;
246a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wenimport java.security.NoSuchAlgorithmException;
256a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wenimport java.util.ArrayList;
266a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wenimport java.util.HashSet;
276a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wenimport java.util.List;
286a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen
296a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen/**
306a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen * Utility library for computing certificate fingerprints. Also includes fields name used by
316a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen * Statement JSON string.
326a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen */
336a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wenpublic final class Utils {
346a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen
356a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen    private Utils() {}
366a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen
376a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen    /**
386a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen     * Field name for namespace.
396a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen     */
406a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen    public static final String NAMESPACE_FIELD = "namespace";
416a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen
426a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen    /**
436a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen     * Supported asset namespaces.
446a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen     */
456a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen    public static final String NAMESPACE_WEB = "web";
466a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen    public static final String NAMESPACE_ANDROID_APP = "android_app";
476a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen
486a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen    /**
496a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen     * Field names in a web asset descriptor.
506a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen     */
516a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen    public static final String WEB_ASSET_FIELD_SITE = "site";
526a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen
536a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen    /**
546a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen     * Field names in a Android app asset descriptor.
556a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen     */
566a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen    public static final String ANDROID_APP_ASSET_FIELD_PACKAGE_NAME = "package_name";
576a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen    public static final String ANDROID_APP_ASSET_FIELD_CERT_FPS = "sha256_cert_fingerprints";
586a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen
596a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen    /**
606a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen     * Field names in a statement.
616a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen     */
626a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen    public static final String ASSET_DESCRIPTOR_FIELD_RELATION = "relation";
636a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen    public static final String ASSET_DESCRIPTOR_FIELD_TARGET = "target";
64aef66921e9bf8147cb0f1444ebdb102016d69f3bJoseph Wen    public static final String DELEGATE_FIELD_DELEGATE = "include";
656a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen
666a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen    private static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
676a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen            'A', 'B', 'C', 'D', 'E', 'F' };
686a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen
696a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen    /**
706a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen     * Joins a list of strings, by placing separator between each string. For example,
716a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen     * {@code joinStrings("; ", Arrays.asList(new String[]{"a", "b", "c"}))} returns
726a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen     * "{@code a; b; c}".
736a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen     */
746a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen    public static String joinStrings(String separator, List<String> strings) {
756a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen        switch(strings.size()) {
766a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen            case 0:
776a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen                return "";
786a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen            case 1:
796a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen                return strings.get(0);
806a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen            default:
816a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen                StringBuilder joiner = new StringBuilder();
826a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen                boolean first = true;
836a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen                for (String field : strings) {
846a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen                    if (first) {
856a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen                        first = false;
866a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen                    } else {
876a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen                        joiner.append(separator);
886a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen                    }
896a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen                    joiner.append(field);
906a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen                }
916a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen                return joiner.toString();
926a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen        }
936a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen    }
946a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen
956a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen    /**
966a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen     * Returns the normalized sha-256 fingerprints of a given package according to the Android
976a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen     * package manager.
986a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen     */
996a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen    public static List<String> getCertFingerprintsFromPackageManager(String packageName,
1006a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen            Context context) throws NameNotFoundException {
1016a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen        Signature[] signatures = context.getPackageManager().getPackageInfo(packageName,
1026a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen                PackageManager.GET_SIGNATURES).signatures;
1036a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen        ArrayList<String> result = new ArrayList<String>(signatures.length);
1046a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen        for (Signature sig : signatures) {
1056a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen            result.add(computeNormalizedSha256Fingerprint(sig.toByteArray()));
1066a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen        }
1076a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen        return result;
1086a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen    }
1096a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen
1106a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen    /**
1116a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen     * Computes the hash of the byte array using the specified algorithm, returning a hex string
1126a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen     * with a colon between each byte.
1136a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen     */
1146a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen    public static String computeNormalizedSha256Fingerprint(byte[] signature) {
1156a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen        MessageDigest digester;
1166a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen        try {
1176a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen            digester = MessageDigest.getInstance("SHA-256");
1186a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen        } catch (NoSuchAlgorithmException e) {
1196a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen            throw new AssertionError("No SHA-256 implementation found.");
1206a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen        }
1216a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen        digester.update(signature);
1226a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen        return byteArrayToHexString(digester.digest());
1236a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen    }
1246a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen
1256a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen    /**
1266a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen     * Returns true if there is at least one common string between the two lists of string.
1276a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen     */
1286a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen    public static boolean hasCommonString(List<String> list1, List<String> list2) {
1296a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen        HashSet<String> set2 = new HashSet<>(list2);
1306a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen        for (String string : list1) {
1316a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen            if (set2.contains(string)) {
1326a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen                return true;
1336a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen            }
1346a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen        }
1356a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen        return false;
1366a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen    }
1376a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen
1386a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen    /**
1396a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen     * Converts the byte array to an lowercase hexadecimal digits String with a colon character (:)
1406a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen     * between each byte.
1416a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen     */
1426a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen    private static String byteArrayToHexString(byte[] array) {
1436a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen        if (array.length == 0) {
1446a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen          return "";
1456a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen        }
1466a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen        char[] buf = new char[array.length * 3 - 1];
1476a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen
1486a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen        int bufIndex = 0;
1496a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen        for (int i = 0; i < array.length; i++) {
1506a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen            byte b = array[i];
1516a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen            if (i > 0) {
1526a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen                buf[bufIndex++] = ':';
1536a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen            }
1546a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen            buf[bufIndex++] = HEX_DIGITS[(b >>> 4) & 0x0F];
1556a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen            buf[bufIndex++] = HEX_DIGITS[b & 0x0F];
1566a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen        }
1576a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen        return new String(buf);
1586a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen    }
1596a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen}
160