Do full verification of apk before talking to installer.

If we are capable of bailing earlier rather than later, then we should. This way,
if a hash doesn't match, the file will be removed and a new download will begin,
as expected. The alternative is to let the installer catch the unmatching hashes.
By then though, it is too late to really do anything meaningfull and it becomes
more difficult to recover in a way that the user would expect.
This commit is contained in:
Peter Serwylo 2016-05-12 14:25:34 +10:00
parent 63807a688d
commit 5c4d23d2d6
2 changed files with 18 additions and 2 deletions

View File

@ -25,6 +25,7 @@ import org.fdroid.fdroid.net.Downloader;
import org.fdroid.fdroid.net.DownloaderService; import org.fdroid.fdroid.net.DownloaderService;
import java.io.File; import java.io.File;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -144,7 +145,7 @@ public class InstallManagerService extends Service {
if (!apkFilePath.exists() || apkFileSize < apk.size) { if (!apkFilePath.exists() || apkFileSize < apk.size) {
Utils.debugLog(TAG, "download " + urlString + " " + apkFilePath); Utils.debugLog(TAG, "download " + urlString + " " + apkFilePath);
DownloaderService.queue(this, urlString); DownloaderService.queue(this, urlString);
} else if (apkFileSize == apk.size) { } else if (apkIsCached(apkFilePath, apk)) {
Utils.debugLog(TAG, "skip download, we have it, straight to install " + urlString + " " + apkFilePath); Utils.debugLog(TAG, "skip download, we have it, straight to install " + urlString + " " + apkFilePath);
sendBroadcast(intent.getData(), Downloader.ACTION_STARTED, apkFilePath); sendBroadcast(intent.getData(), Downloader.ACTION_STARTED, apkFilePath);
sendBroadcast(intent.getData(), Downloader.ACTION_COMPLETE, apkFilePath); sendBroadcast(intent.getData(), Downloader.ACTION_COMPLETE, apkFilePath);
@ -156,6 +157,21 @@ public class InstallManagerService extends Service {
return START_REDELIVER_INTENT; // if killed before completion, retry Intent return START_REDELIVER_INTENT; // if killed before completion, retry Intent
} }
/**
* Verifies the size of the file on disk matches, and then hashes the file to compare with what
* we received from the signed repo (i.e. {@link Apk#hash} and {@link Apk#hashType}).
* Bails out if the file sizes don't match to prevent having to do the work of hashing the file.
*/
private static boolean apkIsCached(File apkFile, Apk apkToCheck) {
try {
return apkFile.length() == apkToCheck.size &&
Installer.verifyApkFile(apkFile, apkToCheck.hash, apkToCheck.hashType);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return false;
}
}
private void sendBroadcast(Uri uri, String action, File file) { private void sendBroadcast(Uri uri, String action, File file) {
Intent intent = new Intent(action); Intent intent = new Intent(action);
intent.setData(uri); intent.setData(uri);

View File

@ -156,7 +156,7 @@ public abstract class Installer {
/** /**
* Checks the APK file against the provided hash, returning whether it is a match. * Checks the APK file against the provided hash, returning whether it is a match.
*/ */
private static boolean verifyApkFile(File apkFile, String hash, String hashType) public static boolean verifyApkFile(File apkFile, String hash, String hashType)
throws NoSuchAlgorithmException { throws NoSuchAlgorithmException {
if (!apkFile.exists()) { if (!apkFile.exists()) {
return false; return false;