diff options
author | Yo Chiang <yochiang@google.com> | 2020-02-06 03:44:08 +0800 |
---|---|---|
committer | Yo Chiang <yochiang@google.com> | 2020-02-19 09:32:36 +0000 |
commit | a86e1ab30a174f0003aeafa662450e443be4dc8a (patch) | |
tree | 7d727c1b7bb63d0a23b6d66a5156d99b4abd8270 /packages/DynamicSystemInstallationService/src | |
parent | 7e0dbc53e33b62a2d0dfd938a7507d6e2c8bd438 (diff) |
Check DSU public key with key revocation list
Throw RevocationListFetchException if failed to fetch key revocation
list.
Throw KeyRevokedException if DSU intent or image public key is revoked.
Throw PublicKeyException if getAvbPublicKey() failed.
Bug: 128892201
Test: adb shell am start-activity \
-n com.android.dynsystem/com.android.dynsystem.VerificationActivity \
-a android.os.image.action.START_INSTALL \
--el KEY_USERDATA_SIZE 8192 \
-d file:///storage/emulated/0/Download/aosp_arm64-dsu_test.zip \
--es KEY_PUBKEY ${IMAGE_KEY}
Test: edit the code so that imageValidationThrowOrWarning() always
Test: throw and observe the logcat and device notification
Change-Id: I33733c019b305c45e7d2511c44ef1d9b446ea52e
Diffstat (limited to 'packages/DynamicSystemInstallationService/src')
2 files changed, 87 insertions, 19 deletions
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java index 9bae223a0a3e..7affe8888628 100644 --- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java +++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java @@ -80,6 +80,7 @@ public class DynamicSystemInstallationService extends Service static final String KEY_ENABLE_WHEN_COMPLETED = "KEY_ENABLE_WHEN_COMPLETED"; static final String KEY_DSU_SLOT = "KEY_DSU_SLOT"; static final String DEFAULT_DSU_SLOT = "dsu"; + static final String KEY_PUBKEY = "KEY_PUBKEY"; /* * Intent actions @@ -267,6 +268,7 @@ public class DynamicSystemInstallationService extends Service long userdataSize = intent.getLongExtra(DynamicSystemClient.KEY_USERDATA_SIZE, 0); mEnableWhenCompleted = intent.getBooleanExtra(KEY_ENABLE_WHEN_COMPLETED, false); String dsuSlot = intent.getStringExtra(KEY_DSU_SLOT); + String publicKey = intent.getStringExtra(KEY_PUBKEY); if (TextUtils.isEmpty(dsuSlot)) { dsuSlot = DEFAULT_DSU_SLOT; @@ -274,7 +276,7 @@ public class DynamicSystemInstallationService extends Service // TODO: better constructor or builder mInstallTask = new InstallationAsyncTask( - url, dsuSlot, systemSize, userdataSize, this, mDynSystem, this); + url, dsuSlot, publicKey, systemSize, userdataSize, this, mDynSystem, this); mInstallTask.execute(); @@ -408,6 +410,10 @@ public class DynamicSystemInstallationService extends Service } private Notification buildNotification(int status, int cause) { + return buildNotification(status, cause, null); + } + + private Notification buildNotification(int status, int cause, Throwable detail) { Notification.Builder builder = new Notification.Builder(this, NOTIFICATION_CHANNEL_ID) .setSmallIcon(R.drawable.ic_system_update_googblue_24dp) .setProgress(0, 0, false); @@ -463,7 +469,12 @@ public class DynamicSystemInstallationService extends Service case STATUS_NOT_STARTED: if (cause != CAUSE_NOT_SPECIFIED && cause != CAUSE_INSTALL_CANCELLED) { - builder.setContentText(getString(R.string.notification_install_failed)); + if (detail instanceof InstallationAsyncTask.ImageValidationException) { + builder.setContentText( + getString(R.string.notification_image_validation_failed)); + } else { + builder.setContentText(getString(R.string.notification_install_failed)); + } } else { // no need to notify the user if the task is not started, or cancelled. } @@ -525,7 +536,7 @@ public class DynamicSystemInstallationService extends Service break; } - Log.d(TAG, "status=" + statusString + ", cause=" + causeString); + Log.d(TAG, "status=" + statusString + ", cause=" + causeString + ", detail=" + detail); boolean notifyOnNotificationBar = true; @@ -538,7 +549,7 @@ public class DynamicSystemInstallationService extends Service } if (notifyOnNotificationBar) { - mNM.notify(NOTIFICATION_ID, buildNotification(status, cause)); + mNM.notify(NOTIFICATION_ID, buildNotification(status, cause, detail)); } for (int i = mClients.size() - 1; i >= 0; i--) { diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java index 438c435ef0e4..7093914aa847 100644 --- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java +++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java @@ -17,6 +17,7 @@ package com.android.dynsystem; import android.content.Context; +import android.gsi.AvbPublicKey; import android.net.Uri; import android.os.AsyncTask; import android.os.MemoryFile; @@ -51,18 +52,46 @@ class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Prog private static final List<String> UNSUPPORTED_PARTITIONS = Arrays.asList("vbmeta", "boot", "userdata", "dtbo", "super_empty", "system_other"); - private class UnsupportedUrlException extends RuntimeException { + private class UnsupportedUrlException extends Exception { private UnsupportedUrlException(String message) { super(message); } } - private class UnsupportedFormatException extends RuntimeException { + private class UnsupportedFormatException extends Exception { private UnsupportedFormatException(String message) { super(message); } } + static class ImageValidationException extends Exception { + ImageValidationException(String message) { + super(message); + } + + ImageValidationException(Throwable cause) { + super(cause); + } + } + + static class RevocationListFetchException extends ImageValidationException { + RevocationListFetchException(Throwable cause) { + super(cause); + } + } + + static class KeyRevokedException extends ImageValidationException { + KeyRevokedException(String message) { + super(message); + } + } + + static class PublicKeyException extends ImageValidationException { + PublicKeyException(String message) { + super(message); + } + } + /** UNSET means the installation is not completed */ static final int RESULT_UNSET = 0; static final int RESULT_OK = 1; @@ -97,6 +126,7 @@ class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Prog private final String mUrl; private final String mDsuSlot; + private final String mPublicKey; private final long mSystemSize; private final long mUserdataSize; private final Context mContext; @@ -115,6 +145,7 @@ class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Prog InstallationAsyncTask( String url, String dsuSlot, + String publicKey, long systemSize, long userdataSize, Context context, @@ -122,6 +153,7 @@ class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Prog ProgressListener listener) { mUrl = url; mDsuSlot = dsuSlot; + mPublicKey = publicKey; mSystemSize = systemSize; mUserdataSize = userdataSize; mContext = context; @@ -157,8 +189,6 @@ class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Prog return null; } - // TODO(yochiang): do post-install public key check (revocation list / boot-ramdisk) - mDynSystem.finishInstallation(); } catch (Exception e) { Log.e(TAG, e.toString(), e); @@ -247,13 +277,16 @@ class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Prog String listUrl = mContext.getString(R.string.key_revocation_list_url); mKeyRevocationList = KeyRevocationList.fromUrl(new URL(listUrl)); } catch (IOException | JSONException e) { - Log.d(TAG, "Failed to fetch Dynamic System Key Revocation List"); mKeyRevocationList = new KeyRevocationList(); - keyRevocationThrowOrWarning(e); + imageValidationThrowOrWarning(new RevocationListFetchException(e)); + } + if (mKeyRevocationList.isRevoked(mPublicKey)) { + imageValidationThrowOrWarning(new KeyRevokedException(mPublicKey)); } } - private void keyRevocationThrowOrWarning(Exception e) throws Exception { + private void imageValidationThrowOrWarning(ImageValidationException e) + throws ImageValidationException { if (mIsNetworkUrl) { throw e; } else { @@ -294,7 +327,8 @@ class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Prog } } - private void installImages() throws IOException, InterruptedException { + private void installImages() + throws IOException, InterruptedException, ImageValidationException { if (mStream != null) { if (mIsZip) { installStreamingZipUpdate(); @@ -306,12 +340,14 @@ class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Prog } } - private void installStreamingGzUpdate() throws IOException, InterruptedException { + private void installStreamingGzUpdate() + throws IOException, InterruptedException, ImageValidationException { Log.d(TAG, "To install a streaming GZ update"); installImage("system", mSystemSize, new GZIPInputStream(mStream), 1); } - private void installStreamingZipUpdate() throws IOException, InterruptedException { + private void installStreamingZipUpdate() + throws IOException, InterruptedException, ImageValidationException { Log.d(TAG, "To install a streaming ZIP update"); ZipInputStream zis = new ZipInputStream(mStream); @@ -330,7 +366,8 @@ class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Prog } } - private void installLocalZipUpdate() throws IOException, InterruptedException { + private void installLocalZipUpdate() + throws IOException, InterruptedException, ImageValidationException { Log.d(TAG, "To install a local ZIP update"); Enumeration<? extends ZipEntry> entries = mZipFile.entries(); @@ -349,8 +386,9 @@ class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Prog } } - private boolean installImageFromAnEntry(ZipEntry entry, InputStream is, - int numInstalledPartitions) throws IOException, InterruptedException { + private boolean installImageFromAnEntry( + ZipEntry entry, InputStream is, int numInstalledPartitions) + throws IOException, InterruptedException, ImageValidationException { String name = entry.getName(); Log.d(TAG, "ZipEntry: " + name); @@ -373,8 +411,9 @@ class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Prog return true; } - private void installImage(String partitionName, long uncompressedSize, InputStream is, - int numInstalledPartitions) throws IOException, InterruptedException { + private void installImage( + String partitionName, long uncompressedSize, InputStream is, int numInstalledPartitions) + throws IOException, InterruptedException, ImageValidationException { SparseInputStream sis = new SparseInputStream(new BufferedInputStream(is)); @@ -445,6 +484,24 @@ class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Prog publishProgress(progress); } } + + AvbPublicKey avbPublicKey = new AvbPublicKey(); + if (!mInstallationSession.getAvbPublicKey(avbPublicKey)) { + imageValidationThrowOrWarning(new PublicKeyException("getAvbPublicKey() failed")); + } else { + String publicKey = toHexString(avbPublicKey.sha1); + if (mKeyRevocationList.isRevoked(publicKey)) { + imageValidationThrowOrWarning(new KeyRevokedException(publicKey)); + } + } + } + + private static String toHexString(byte[] bytes) { + StringBuilder sb = new StringBuilder(); + for (byte b : bytes) { + sb.append(String.format("%02x", b)); + } + return sb.toString(); } private void close() { |