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