/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.statementservice.retriever;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
/**
* Utility library for computing certificate fingerprints. Also includes fields name used by
* Statement JSON string.
*/
public final class Utils {
private Utils() {}
/**
* Field name for namespace.
*/
public static final String NAMESPACE_FIELD = "namespace";
/**
* Supported asset namespaces.
*/
public static final String NAMESPACE_WEB = "web";
public static final String NAMESPACE_ANDROID_APP = "android_app";
/**
* Field names in a web asset descriptor.
*/
public static final String WEB_ASSET_FIELD_SITE = "site";
/**
* Field names in a Android app asset descriptor.
*/
public static final String ANDROID_APP_ASSET_FIELD_PACKAGE_NAME = "package_name";
public static final String ANDROID_APP_ASSET_FIELD_CERT_FPS = "sha256_cert_fingerprints";
/**
* Field names in a statement.
*/
public static final String ASSET_DESCRIPTOR_FIELD_RELATION = "relation";
public static final String ASSET_DESCRIPTOR_FIELD_TARGET = "target";
public static final String DELEGATE_FIELD_DELEGATE = "include";
private static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'A', 'B', 'C', 'D', 'E', 'F' };
/**
* Joins a list of strings, by placing separator between each string. For example,
* {@code joinStrings("; ", Arrays.asList(new String[]{"a", "b", "c"}))} returns
* "{@code a; b; c}".
*/
public static String joinStrings(String separator, List<String> strings) {
switch(strings.size()) {
case 0:
return "";
case 1:
return strings.get(0);
default:
StringBuilder joiner = new StringBuilder();
boolean first = true;
for (String field : strings) {
if (first) {
first = false;
} else {
joiner.append(separator);
}
joiner.append(field);
}
return joiner.toString();
}
}
/**
* Returns the normalized sha-256 fingerprints of a given package according to the Android
* package manager.
*/
public static List<String> getCertFingerprintsFromPackageManager(String packageName,
Context context) throws NameNotFoundException {
Signature[] signatures = context.getPackageManager().getPackageInfo(packageName,
PackageManager.GET_SIGNATURES).signatures;
ArrayList<String> result = new ArrayList<String>(signatures.length);
for (Signature sig : signatures) {
result.add(computeNormalizedSha256Fingerprint(sig.toByteArray()));
}
return result;
}
/**
* Computes the hash of the byte array using the specified algorithm, returning a hex string
* with a colon between each byte.
*/
public static String computeNormalizedSha256Fingerprint(byte[] signature) {
MessageDigest digester;
try {
digester = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
throw new AssertionError("No SHA-256 implementation found.");
}
digester.update(signature);
return byteArrayToHexString(digester.digest());
}
/**
* Returns true if there is at least one common string between the two lists of string.
*/
public static boolean hasCommonString(List<String> list1, List<String> list2) {
HashSet<String> set2 = new HashSet<>(list2);
for (String string : list1) {
if (set2.contains(string)) {
return true;
}
}
return false;
}
/**
* Converts the byte array to an lowercase hexadecimal digits String with a colon character (:)
* between each byte.
*/
private static String byteArrayToHexString(byte[] array) {
if (array.length == 0) {
return "";
}
char[] buf = new char[array.length * 3 - 1];
int bufIndex = 0;
for (int i = 0; i < array.length; i++) {
byte b = array[i];
if (i > 0) {
buf[bufIndex++] = ':';
}
buf[bufIndex++] = HEX_DIGITS[(b >>> 4) & 0x0F];
buf[bufIndex++] = HEX_DIGITS[b & 0x0F];
}
return new String(buf);
}
}