Skip to content
Snippets Groups Projects
Commit 9bcc5fa9 authored by Tianjie Xu's avatar Tianjie Xu Committed by Automerger Merge Worker
Browse files

Merge "Support parsing legacy reboot escrow data" am: 8d380420 am: dd36d037

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1563080

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: Ia0acc30c79574ff6a00cb4cc5334b82d0e9b01e5
parents e0f72d91 dd36d037
No related branches found
No related tags found
No related merge requests found
......@@ -35,6 +35,12 @@ class RebootEscrowData {
*/
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 @@ class RebootEscrowData {
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 @@ class RebootEscrowData {
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,
......
......@@ -146,6 +146,7 @@ class RebootEscrowManager {
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 @@ class RebootEscrowManager {
// 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 @@ class RebootEscrowManager {
return;
}
if (kk == null || escrowKey == null) {
if (escrowKey == null) {
onGetRebootEscrowKeyFailed(users);
return;
}
......
......@@ -136,6 +136,11 @@ class RebootEscrowProviderServerBasedImpl implements RebootEscrowProviderInterfa
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 {
......
......@@ -19,19 +19,17 @@ package com.android.server.locksettings;
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 @@ public class RebootEscrowDataTest {
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 @@ public class RebootEscrowDataTest {
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));
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment