Home | History | Annotate | Download | only in retriever
      1 /*
      2  * Copyright (C) 2015 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 package com.android.statementservice.retriever;
     17 
     18 import android.content.Context;
     19 import android.content.pm.PackageManager;
     20 import android.content.pm.PackageManager.NameNotFoundException;
     21 import android.content.pm.Signature;
     22 
     23 import java.security.MessageDigest;
     24 import java.security.NoSuchAlgorithmException;
     25 import java.util.ArrayList;
     26 import java.util.HashSet;
     27 import java.util.List;
     28 
     29 /**
     30  * Utility library for computing certificate fingerprints. Also includes fields name used by
     31  * Statement JSON string.
     32  */
     33 public final class Utils {
     34 
     35     private Utils() {}
     36 
     37     /**
     38      * Field name for namespace.
     39      */
     40     public static final String NAMESPACE_FIELD = "namespace";
     41 
     42     /**
     43      * Supported asset namespaces.
     44      */
     45     public static final String NAMESPACE_WEB = "web";
     46     public static final String NAMESPACE_ANDROID_APP = "android_app";
     47 
     48     /**
     49      * Field names in a web asset descriptor.
     50      */
     51     public static final String WEB_ASSET_FIELD_SITE = "site";
     52 
     53     /**
     54      * Field names in a Android app asset descriptor.
     55      */
     56     public static final String ANDROID_APP_ASSET_FIELD_PACKAGE_NAME = "package_name";
     57     public static final String ANDROID_APP_ASSET_FIELD_CERT_FPS = "sha256_cert_fingerprints";
     58 
     59     /**
     60      * Field names in a statement.
     61      */
     62     public static final String ASSET_DESCRIPTOR_FIELD_RELATION = "relation";
     63     public static final String ASSET_DESCRIPTOR_FIELD_TARGET = "target";
     64     public static final String DELEGATE_FIELD_DELEGATE = "include";
     65 
     66     private static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
     67             'A', 'B', 'C', 'D', 'E', 'F' };
     68 
     69     /**
     70      * Joins a list of strings, by placing separator between each string. For example,
     71      * {@code joinStrings("; ", Arrays.asList(new String[]{"a", "b", "c"}))} returns
     72      * "{@code a; b; c}".
     73      */
     74     public static String joinStrings(String separator, List<String> strings) {
     75         switch(strings.size()) {
     76             case 0:
     77                 return "";
     78             case 1:
     79                 return strings.get(0);
     80             default:
     81                 StringBuilder joiner = new StringBuilder();
     82                 boolean first = true;
     83                 for (String field : strings) {
     84                     if (first) {
     85                         first = false;
     86                     } else {
     87                         joiner.append(separator);
     88                     }
     89                     joiner.append(field);
     90                 }
     91                 return joiner.toString();
     92         }
     93     }
     94 
     95     /**
     96      * Returns the normalized sha-256 fingerprints of a given package according to the Android
     97      * package manager.
     98      */
     99     public static List<String> getCertFingerprintsFromPackageManager(String packageName,
    100             Context context) throws NameNotFoundException {
    101         Signature[] signatures = context.getPackageManager().getPackageInfo(packageName,
    102                 PackageManager.GET_SIGNATURES).signatures;
    103         ArrayList<String> result = new ArrayList<String>(signatures.length);
    104         for (Signature sig : signatures) {
    105             result.add(computeNormalizedSha256Fingerprint(sig.toByteArray()));
    106         }
    107         return result;
    108     }
    109 
    110     /**
    111      * Computes the hash of the byte array using the specified algorithm, returning a hex string
    112      * with a colon between each byte.
    113      */
    114     public static String computeNormalizedSha256Fingerprint(byte[] signature) {
    115         MessageDigest digester;
    116         try {
    117             digester = MessageDigest.getInstance("SHA-256");
    118         } catch (NoSuchAlgorithmException e) {
    119             throw new AssertionError("No SHA-256 implementation found.");
    120         }
    121         digester.update(signature);
    122         return byteArrayToHexString(digester.digest());
    123     }
    124 
    125     /**
    126      * Returns true if there is at least one common string between the two lists of string.
    127      */
    128     public static boolean hasCommonString(List<String> list1, List<String> list2) {
    129         HashSet<String> set2 = new HashSet<>(list2);
    130         for (String string : list1) {
    131             if (set2.contains(string)) {
    132                 return true;
    133             }
    134         }
    135         return false;
    136     }
    137 
    138     /**
    139      * Converts the byte array to an lowercase hexadecimal digits String with a colon character (:)
    140      * between each byte.
    141      */
    142     private static String byteArrayToHexString(byte[] array) {
    143         if (array.length == 0) {
    144           return "";
    145         }
    146         char[] buf = new char[array.length * 3 - 1];
    147 
    148         int bufIndex = 0;
    149         for (int i = 0; i < array.length; i++) {
    150             byte b = array[i];
    151             if (i > 0) {
    152                 buf[bufIndex++] = ':';
    153             }
    154             buf[bufIndex++] = HEX_DIGITS[(b >>> 4) & 0x0F];
    155             buf[bufIndex++] = HEX_DIGITS[b & 0x0F];
    156         }
    157         return new String(buf);
    158     }
    159 }
    160