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 Utils()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 */ joinStrings(String separator, List<String> strings)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 */ getCertFingerprintsFromPackageManager(String packageName, Context context)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 */ computeNormalizedSha256Fingerprint(byte[] signature)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 */ hasCommonString(List<String> list1, List<String> list2)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 */ byteArrayToHexString(byte[] array)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