Merge "Support parsing legacy reboot escrow data" am: 8d38042009 am: dd36d03799
Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1563080
MUST ONLY BE SUBMITTED BY AUTOMERGER
Change-Id: Ia0acc30c79574ff6a00cb4cc5334b82d0e9b01e5
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowData.java b/services/core/java/com/android/server/locksettings/RebootEscrowData.java
index 38eeb88..af0774c 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowData.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowData.java
@@ -35,6 +35,12 @@
*/
private static final int CURRENT_VERSION = 2;
+ /**
+ * This is the legacy version of the escrow data format for R builds. The escrow data is only
+ * encrypted by the escrow key, without additional wrap of another key from keystore.
+ */
+ private static final int LEGACY_SINGLE_ENCRYPTED_VERSION = 1;
+
private RebootEscrowData(byte spVersion, byte[] syntheticPassword, byte[] blob,
RebootEscrowKey key) {
mSpVersion = spVersion;
@@ -64,6 +70,19 @@
return mKey;
}
+ private static byte[] decryptBlobCurrentVersion(SecretKey kk, RebootEscrowKey ks,
+ DataInputStream dis) throws IOException {
+ if (kk == null) {
+ throw new IOException("Failed to find wrapper key in keystore, cannot decrypt the"
+ + " escrow data");
+ }
+
+ // Decrypt the blob with the key from keystore first, then decrypt again with the reboot
+ // escrow key.
+ byte[] ksEncryptedBlob = AesEncryptionUtil.decrypt(kk, dis);
+ return AesEncryptionUtil.decrypt(ks.getKey(), ksEncryptedBlob);
+ }
+
static RebootEscrowData fromEncryptedData(RebootEscrowKey ks, byte[] blob, SecretKey kk)
throws IOException {
Objects.requireNonNull(ks);
@@ -71,17 +90,20 @@
DataInputStream dis = new DataInputStream(new ByteArrayInputStream(blob));
int version = dis.readInt();
- if (version != CURRENT_VERSION) {
- throw new IOException("Unsupported version " + version);
- }
byte spVersion = dis.readByte();
-
- // Decrypt the blob with the key from keystore first, then decrypt again with the reboot
- // escrow key.
- byte[] ksEncryptedBlob = AesEncryptionUtil.decrypt(kk, dis);
- final byte[] syntheticPassword = AesEncryptionUtil.decrypt(ks.getKey(), ksEncryptedBlob);
-
- return new RebootEscrowData(spVersion, syntheticPassword, blob, ks);
+ switch (version) {
+ case CURRENT_VERSION: {
+ byte[] syntheticPassword = decryptBlobCurrentVersion(kk, ks, dis);
+ return new RebootEscrowData(spVersion, syntheticPassword, blob, ks);
+ }
+ case LEGACY_SINGLE_ENCRYPTED_VERSION: {
+ // Decrypt the blob with the escrow key directly.
+ byte[] syntheticPassword = AesEncryptionUtil.decrypt(ks.getKey(), dis);
+ return new RebootEscrowData(spVersion, syntheticPassword, blob, ks);
+ }
+ default:
+ throw new IOException("Unsupported version " + version);
+ }
}
static RebootEscrowData fromSyntheticPassword(RebootEscrowKey ks, byte spVersion,
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
index 53b62ca..30ea555 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
@@ -146,6 +146,7 @@
RebootEscrowProviderInterface rebootEscrowProvider;
if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_OTA,
"server_based_ror_enabled", false)) {
+ Slog.i(TAG, "Using server based resume on reboot");
rebootEscrowProvider = new RebootEscrowProviderServerBasedImpl(mContext, mStorage);
} else {
rebootEscrowProvider = new RebootEscrowProviderHalImpl();
@@ -272,6 +273,10 @@
// generated before reboot. Note that we will clear the escrow key even if the keystore key
// is null.
SecretKey kk = mKeyStoreManager.getKeyStoreEncryptionKey();
+ if (kk == null) {
+ Slog.i(TAG, "Failed to load the key for resume on reboot from key store.");
+ }
+
RebootEscrowKey escrowKey;
try {
escrowKey = getAndClearRebootEscrowKey(kk);
@@ -281,7 +286,7 @@
return;
}
- if (kk == null || escrowKey == null) {
+ if (escrowKey == null) {
onGetRebootEscrowKeyFailed(users);
return;
}
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java b/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java
index 9d09637..b3b4546 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java
@@ -136,6 +136,11 @@
Slog.w(TAG, "Failed to read reboot escrow server blob from storage");
return null;
}
+ if (decryptionKey == null) {
+ Slog.w(TAG, "Failed to decrypt the escrow key; decryption key from keystore is"
+ + " null.");
+ return null;
+ }
Slog.i(TAG, "Loaded reboot escrow server blob from storage");
try {
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowDataTest.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowDataTest.java
index 32445fd..2eedc32 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowDataTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowDataTest.java
@@ -19,19 +19,17 @@
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
-import android.security.keystore.KeyGenParameterSpec;
-import android.security.keystore.KeyProperties;
-
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.security.GeneralSecurityException;
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
-import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
/**
* atest FrameworksServicesTests:RebootEscrowDataTest
@@ -41,22 +39,18 @@
private RebootEscrowKey mKey;
private SecretKey mKeyStoreEncryptionKey;
- private SecretKey generateNewRebootEscrowEncryptionKey() throws GeneralSecurityException {
- KeyGenerator generator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES);
- generator.init(new KeyGenParameterSpec.Builder(
- "reboot_escrow_data_test_key",
- KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
- .setKeySize(256)
- .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
- .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
- .build());
- return generator.generateKey();
- }
+ // Hex encoding of a randomly generated AES key for test.
+ private static final byte[] TEST_AES_KEY = new byte[] {
+ 0x44, 0x74, 0x61, 0x54, 0x29, 0x74, 0x37, 0x61,
+ 0x48, 0x19, 0x12, 0x54, 0x13, 0x13, 0x52, 0x31,
+ 0x70, 0x70, 0x75, 0x25, 0x27, 0x31, 0x49, 0x09,
+ 0x26, 0x52, 0x72, 0x63, 0x63, 0x61, 0x78, 0x23,
+ };
@Before
public void generateKey() throws Exception {
mKey = RebootEscrowKey.generate();
- mKeyStoreEncryptionKey = generateNewRebootEscrowEncryptionKey();
+ mKeyStoreEncryptionKey = new SecretKeySpec(TEST_AES_KEY, "AES");
}
private static byte[] getTestSp() {
@@ -114,4 +108,23 @@
assertThat(decrypted, is(testSp));
}
+ @Test
+ public void fromEncryptedData_legacyVersion_success() throws Exception {
+ byte[] testSp = getTestSp();
+ byte[] ksEncryptedBlob = AesEncryptionUtil.encrypt(mKey.getKey(), testSp);
+
+ // Write a legacy blob encrypted only by k_s.
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ DataOutputStream dos = new DataOutputStream(bos);
+ dos.writeInt(1);
+ dos.writeByte(3);
+ dos.write(ksEncryptedBlob);
+ byte[] legacyBlob = bos.toByteArray();
+
+ RebootEscrowData actual = RebootEscrowData.fromEncryptedData(mKey, legacyBlob, null);
+
+ assertThat(actual.getSpVersion(), is((byte) 3));
+ assertThat(actual.getKey().getKeyBytes(), is(mKey.getKeyBytes()));
+ assertThat(actual.getSyntheticPassword(), is(testSp));
+ }
}