refactor into reusable static method for checking file hashes

This takes the APK file hash checker and turns it into a generic static
utility method for checking that a given file matches a given hash.  This
will be needed as F-Droid handles other file types, like OBB and media.
This commit is contained in:
Hans-Christoph Steiner 2016-09-30 12:46:58 +02:00
parent a5e6dad9bf
commit 4c4aef5314
2 changed files with 40 additions and 48 deletions

View File

@ -97,6 +97,21 @@ public class Hasher {
return hashCache.equals(otherHash.toLowerCase(Locale.ENGLISH)); return hashCache.equals(otherHash.toLowerCase(Locale.ENGLISH));
} }
/**
* Checks the file against the provided hash, returning whether it is a match.
*/
public static boolean isFileMatchingHash(File file, String hash, String hashType) {
if (!file.exists()) {
return false;
}
try {
Hasher hasher = new Hasher(hashType, file);
return hasher.match(hash);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
public static String hex(Certificate cert) { public static String hex(Certificate cert) {
byte[] encoded; byte[] encoded;
try { try {

View File

@ -31,7 +31,6 @@ import org.fdroid.fdroid.data.SanitizedFile;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.security.NoSuchAlgorithmException;
public class ApkCache { public class ApkCache {
@ -45,23 +44,16 @@ public class ApkCache {
*/ */
public static SanitizedFile copyApkFromCacheToFiles(Context context, File apkFile, Apk expectedApk) public static SanitizedFile copyApkFromCacheToFiles(Context context, File apkFile, Apk expectedApk)
throws IOException { throws IOException {
SanitizedFile sanitizedApkFile = null; SanitizedFile sanitizedApkFile = SanitizedFile.knownSanitized(
try {
sanitizedApkFile = SanitizedFile.knownSanitized(
File.createTempFile("install-", ".apk", context.getFilesDir())); File.createTempFile("install-", ".apk", context.getFilesDir()));
FileUtils.copyFile(apkFile, sanitizedApkFile); FileUtils.copyFile(apkFile, sanitizedApkFile);
// verify copied file's hash with expected hash from Apk class // verify copied file's hash with expected hash from Apk class
if (!verifyApkFile(sanitizedApkFile, expectedApk.hash, expectedApk.hashType)) { if (!Hasher.isFileMatchingHash(sanitizedApkFile, expectedApk.hash, expectedApk.hashType)) {
FileUtils.deleteQuietly(apkFile); FileUtils.deleteQuietly(apkFile);
throw new IOException(apkFile + " failed to verify!"); throw new IOException(apkFile + " failed to verify!");
} }
return sanitizedApkFile;
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
} finally {
// 20 minutes the start of the install process, delete the file // 20 minutes the start of the install process, delete the file
final File apkToDelete = sanitizedApkFile; final File apkToDelete = sanitizedApkFile;
new Thread() { new Thread() {
@ -76,19 +68,8 @@ public class ApkCache {
} }
} }
}.start(); }.start();
}
}
/** return sanitizedApkFile;
* Checks the APK file against the provided hash, returning whether it is a match.
*/
private static boolean verifyApkFile(File apkFile, String hash, String hashType)
throws NoSuchAlgorithmException {
if (!apkFile.exists()) {
return false;
}
Hasher hasher = new Hasher(hashType, apkFile);
return hasher.match(hash);
} }
/** /**
@ -108,19 +89,15 @@ public class ApkCache {
* Bails out if the file sizes don't match to prevent having to do the work of hashing the file. * Bails out if the file sizes don't match to prevent having to do the work of hashing the file.
*/ */
public static boolean apkIsCached(File apkFile, Apk apkToCheck) { public static boolean apkIsCached(File apkFile, Apk apkToCheck) {
try {
return apkFile.length() == apkToCheck.size && return apkFile.length() == apkToCheck.size &&
verifyApkFile(apkFile, apkToCheck.hash, apkToCheck.hashType); Hasher.isFileMatchingHash(apkFile, apkToCheck.hash, apkToCheck.hashType);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
} }
/** /**
* This location is only for caching, do not install directly from this location * This location is only for caching, do not install directly from this location
* because if the file is on the External Storage, any other app could swap out * because if the file is on the External Storage, any other app could swap out
* the APK while the install was in process, allowing malware to install things. * the APK while the install was in process, allowing malware to install things.
* Using {@link Installer#installPackage(Uri, Uri, Apk)} * Using {@link Installer#installPackage(Uri, Uri)}
* is fine since that does the right thing. * is fine since that does the right thing.
*/ */
public static File getApkCacheDir(Context context) { public static File getApkCacheDir(Context context) {