Merge "Clean up protector key terminology"

This commit is contained in:
Eric Biggers
2022-08-02 18:07:46 +00:00
committed by Android (Google) Code Review
5 changed files with 63 additions and 51 deletions

View File

@@ -166,10 +166,6 @@ public class LockPatternUtils {
private static final String ENABLED_TRUST_AGENTS = "lockscreen.enabledtrustagents";
private static final String IS_TRUST_USUALLY_MANAGED = "lockscreen.istrustusuallymanaged";
public static final String PROFILE_KEY_NAME_ENCRYPT = "profile_key_name_encrypt_";
public static final String PROFILE_KEY_NAME_DECRYPT = "profile_key_name_decrypt_";
public static final String SYNTHETIC_PASSWORD_KEY_PREFIX = "synthetic_password_";
public static final String CURRENT_LSKF_BASED_PROTECTOR_ID_KEY = "sp-handle";
public static final String PASSWORD_HISTORY_DELIMITER = ",";

View File

@@ -35,8 +35,6 @@ import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTE
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN;
import static com.android.internal.widget.LockPatternUtils.CURRENT_LSKF_BASED_PROTECTOR_ID_KEY;
import static com.android.internal.widget.LockPatternUtils.EscrowTokenStateChangeCallback;
import static com.android.internal.widget.LockPatternUtils.PROFILE_KEY_NAME_DECRYPT;
import static com.android.internal.widget.LockPatternUtils.PROFILE_KEY_NAME_ENCRYPT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE;
import static com.android.internal.widget.LockPatternUtils.USER_FRP;
@@ -211,6 +209,9 @@ public class LockSettingsService extends ILockSettings.Stub {
// user's credential must be presented again, e.g. via ConfirmLockPattern/ConfirmLockPassword.
private static final int GK_PW_HANDLE_STORE_DURATION_MS = 10 * 60 * 1000; // 10 minutes
private static final String PROFILE_KEY_NAME_ENCRYPT = "profile_key_name_encrypt_";
private static final String PROFILE_KEY_NAME_DECRYPT = "profile_key_name_decrypt_";
// Order of holding lock: mSeparateChallengeLock -> mSpManager -> this
// Do not call into ActivityManager while holding mSpManager lock.
private final Object mSeparateChallengeLock = new Object();

View File

@@ -127,18 +127,20 @@ public class SyntheticPasswordCrypto {
}
/**
* Decrypt a legacy SP blob which did the Keystore and software encryption layers in the wrong
* Decrypts a legacy SP blob which did the Keystore and software encryption layers in the wrong
* order.
*/
public static byte[] decryptBlobV1(String keyAlias, byte[] blob, byte[] protectorSecret) {
public static byte[] decryptBlobV1(String protectorKeyAlias, byte[] blob,
byte[] protectorSecret) {
try {
KeyStore keyStore = getKeyStore();
SecretKey keyStoreKey = (SecretKey) keyStore.getKey(keyAlias, null);
if (keyStoreKey == null) {
throw new IllegalStateException("SP key is missing: " + keyAlias);
SecretKey protectorKey = (SecretKey) keyStore.getKey(protectorKeyAlias, null);
if (protectorKey == null) {
throw new IllegalStateException("SP protector key is missing: "
+ protectorKeyAlias);
}
byte[] intermediate = decrypt(protectorSecret, PROTECTOR_SECRET_PERSONALIZATION, blob);
return decrypt(keyStoreKey, intermediate);
return decrypt(protectorKey, intermediate);
} catch (Exception e) {
Slog.e(TAG, "Failed to decrypt V1 blob", e);
throw new IllegalStateException("Failed to decrypt blob", e);
@@ -163,15 +165,17 @@ public class SyntheticPasswordCrypto {
/**
* Decrypts an SP blob that was created by {@link #createBlob}.
*/
public static byte[] decryptBlob(String keyAlias, byte[] blob, byte[] protectorSecret) {
public static byte[] decryptBlob(String protectorKeyAlias, byte[] blob,
byte[] protectorSecret) {
try {
final KeyStore keyStore = getKeyStore();
SecretKey keyStoreKey = (SecretKey) keyStore.getKey(keyAlias, null);
if (keyStoreKey == null) {
throw new IllegalStateException("SP key is missing: " + keyAlias);
SecretKey protectorKey = (SecretKey) keyStore.getKey(protectorKeyAlias, null);
if (protectorKey == null) {
throw new IllegalStateException("SP protector key is missing: "
+ protectorKeyAlias);
}
byte[] intermediate = decrypt(keyStoreKey, blob);
byte[] intermediate = decrypt(protectorKey, blob);
return decrypt(protectorSecret, PROTECTOR_SECRET_PERSONALIZATION, intermediate);
} catch (CertificateException | IOException | BadPaddingException
| IllegalBlockSizeException
@@ -185,20 +189,21 @@ public class SyntheticPasswordCrypto {
/**
* Creates a new SP blob by encrypting the given data. Two encryption layers are applied: an
* inner layer using a hash of protectorSecret as the key, and an outer layer using a new
* Keystore key with the given alias and optionally bound to a SID.
* inner layer using a hash of protectorSecret as the key, and an outer layer using the
* protector key, which is a Keystore key that is optionally bound to a SID. This method
* creates the protector key and stores it under protectorKeyAlias.
*
* The reason we use a layer of software encryption, instead of using protectorSecret as the
* applicationId of the Keystore key, is to work around buggy KeyMint implementations that don't
* cryptographically bind the applicationId to the key. The Keystore layer has to be the outer
* layer, so that LSKF verification is ratelimited by Gatekeeper when Weaver is unavailable.
*/
public static byte[] createBlob(String keyAlias, byte[] data, byte[] protectorSecret,
public static byte[] createBlob(String protectorKeyAlias, byte[] data, byte[] protectorSecret,
long sid) {
try {
KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES);
keyGenerator.init(AES_GCM_KEY_SIZE * 8, new SecureRandom());
SecretKey keyStoreKey = keyGenerator.generateKey();
SecretKey protectorKey = keyGenerator.generateKey();
final KeyStore keyStore = getKeyStore();
KeyProtection.Builder builder = new KeyProtection.Builder(KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
@@ -212,9 +217,9 @@ public class SyntheticPasswordCrypto {
final KeyProtection protNonRollbackResistant = builder.build();
builder.setRollbackResistant(true);
final KeyProtection protRollbackResistant = builder.build();
final KeyStore.SecretKeyEntry entry = new KeyStore.SecretKeyEntry(keyStoreKey);
final KeyStore.SecretKeyEntry entry = new KeyStore.SecretKeyEntry(protectorKey);
try {
keyStore.setEntry(keyAlias, entry, protRollbackResistant);
keyStore.setEntry(protectorKeyAlias, entry, protRollbackResistant);
Slog.i(TAG, "Using rollback-resistant key");
} catch (KeyStoreException e) {
if (!(e.getCause() instanceof android.security.KeyStoreException)) {
@@ -226,11 +231,11 @@ public class SyntheticPasswordCrypto {
}
Slog.w(TAG, "Rollback-resistant keys unavailable. Falling back to "
+ "non-rollback-resistant key");
keyStore.setEntry(keyAlias, entry, protNonRollbackResistant);
keyStore.setEntry(protectorKeyAlias, entry, protNonRollbackResistant);
}
byte[] intermediate = encrypt(protectorSecret, PROTECTOR_SECRET_PERSONALIZATION, data);
return encrypt(keyStoreKey, intermediate);
return encrypt(protectorKey, intermediate);
} catch (CertificateException | IOException | BadPaddingException
| IllegalBlockSizeException
| KeyStoreException | NoSuchPaddingException | NoSuchAlgorithmException
@@ -241,15 +246,15 @@ public class SyntheticPasswordCrypto {
}
}
public static void destroyBlobKey(String keyAlias) {
public static void destroyProtectorKey(String keyAlias) {
KeyStore keyStore;
try {
keyStore = getKeyStore();
keyStore.deleteEntry(keyAlias);
Slog.i(TAG, "SP key deleted: " + keyAlias);
Slog.i(TAG, "Deleted SP protector key " + keyAlias);
} catch (KeyStoreException | NoSuchAlgorithmException | CertificateException
| IOException e) {
Slog.e(TAG, "Failed to destroy blob", e);
Slog.e(TAG, "Failed to delete SP protector key " + keyAlias, e);
}
}

View File

@@ -135,6 +135,8 @@ public class SyntheticPasswordManager {
private static final byte PROTECTOR_TYPE_STRONG_TOKEN_BASED = 1;
private static final byte PROTECTOR_TYPE_WEAK_TOKEN_BASED = 2;
private static final String PROTECTOR_KEY_ALIAS_PREFIX = "synthetic_password_";
// The security strength of the synthetic password, in bytes
private static final int SYNTHETIC_PASSWORD_SECURITY_STRENGTH = 256 / 8;
@@ -583,7 +585,7 @@ public class SyntheticPasswordManager {
for (long protectorId : mStorage.listSyntheticPasswordProtectorsForUser(SP_BLOB_NAME,
userId)) {
destroyWeaverSlot(protectorId, userId);
destroySPBlobKey(getKeyName(protectorId));
destroyProtectorKey(getProtectorKeyAlias(protectorId));
}
// Remove potential persistent state (in RPMB), to prevent them from accumulating and
// causing problems.
@@ -999,7 +1001,8 @@ public class SyntheticPasswordManager {
} else {
spSecret = sp.getSyntheticPassword();
}
byte[] content = createSPBlob(getKeyName(protectorId), spSecret, protectorSecret, sid);
byte[] content = createSpBlob(getProtectorKeyAlias(protectorId), spSecret, protectorSecret,
sid);
/*
* We can upgrade from v1 to v2 because that's just a change in the way that
* the SP is stored. However, we can't upgrade to v3 because that is a change
@@ -1210,10 +1213,11 @@ public class SyntheticPasswordManager {
}
final byte[] spSecret;
if (blob.mVersion == SYNTHETIC_PASSWORD_VERSION_V1) {
spSecret = SyntheticPasswordCrypto.decryptBlobV1(getKeyName(protectorId), blob.mContent,
protectorSecret);
spSecret = SyntheticPasswordCrypto.decryptBlobV1(getProtectorKeyAlias(protectorId),
blob.mContent, protectorSecret);
} else {
spSecret = decryptSPBlob(getKeyName(protectorId), blob.mContent, protectorSecret);
spSecret = decryptSpBlob(getProtectorKeyAlias(protectorId), blob.mContent,
protectorSecret);
}
if (spSecret == null) {
Slog.e(TAG, "Fail to decrypt SP for user " + userId);
@@ -1337,7 +1341,7 @@ public class SyntheticPasswordManager {
private void destroyProtectorCommon(long protectorId, int userId) {
destroyState(SP_BLOB_NAME, protectorId, userId);
destroySPBlobKey(getKeyName(protectorId));
destroyProtectorKey(getProtectorKeyAlias(protectorId));
destroyState(SECDISCARDABLE_NAME, protectorId, userId);
if (hasState(WEAVER_SLOT_NAME, protectorId, userId)) {
destroyWeaverSlot(protectorId, userId);
@@ -1422,17 +1426,20 @@ public class SyntheticPasswordManager {
mStorage.deleteSyntheticPasswordState(userId, protectorId, stateName);
}
protected byte[] decryptSPBlob(String blobKeyName, byte[] blob, byte[] protectorSecret) {
return SyntheticPasswordCrypto.decryptBlob(blobKeyName, blob, protectorSecret);
@VisibleForTesting
protected byte[] decryptSpBlob(String protectorKeyAlias, byte[] blob, byte[] protectorSecret) {
return SyntheticPasswordCrypto.decryptBlob(protectorKeyAlias, blob, protectorSecret);
}
protected byte[] createSPBlob(String blobKeyName, byte[] data, byte[] protectorSecret,
@VisibleForTesting
protected byte[] createSpBlob(String protectorKeyAlias, byte[] data, byte[] protectorSecret,
long sid) {
return SyntheticPasswordCrypto.createBlob(blobKeyName, data, protectorSecret, sid);
return SyntheticPasswordCrypto.createBlob(protectorKeyAlias, data, protectorSecret, sid);
}
protected void destroySPBlobKey(String keyAlias) {
SyntheticPasswordCrypto.destroyBlobKey(keyAlias);
@VisibleForTesting
protected void destroyProtectorKey(String keyAlias) {
SyntheticPasswordCrypto.destroyProtectorKey(keyAlias);
}
public static long generateProtectorId() {
@@ -1457,8 +1464,8 @@ public class SyntheticPasswordManager {
}
}
private String getKeyName(long protectorId) {
return String.format("%s%x", LockPatternUtils.SYNTHETIC_PASSWORD_KEY_PREFIX, protectorId);
private String getProtectorKeyAlias(long protectorId) {
return String.format("%s%x", PROTECTOR_KEY_ALIAS_PREFIX, protectorId);
}
private byte[] computePasswordToken(LockscreenCredential credential, PasswordData data) {
@@ -1511,7 +1518,7 @@ public class SyntheticPasswordManager {
}
/**
* Migrate all existing SP keystore keys from uid 1000 app domain to LSS selinux domain
* Migrates all existing SP protector keys from uid 1000 app domain to LSS selinux domain.
*/
public boolean migrateKeyNamespace() {
boolean success = true;
@@ -1519,7 +1526,8 @@ public class SyntheticPasswordManager {
mStorage.listSyntheticPasswordProtectorsForAllUsers(SP_BLOB_NAME);
for (List<Long> userProtectors : allProtectors.values()) {
for (long protectorId : userProtectors) {
success &= SyntheticPasswordCrypto.migrateLockSettingsKey(getKeyName(protectorId));
success &= SyntheticPasswordCrypto.migrateLockSettingsKey(
getProtectorKeyAlias(protectorId));
}
}
return success;

View File

@@ -44,12 +44,14 @@ public class MockSyntheticPasswordManager extends SyntheticPasswordManager {
mGateKeeper = gatekeeper;
}
private ArrayMap<String, byte[]> mBlobs = new ArrayMap<>();
private final ArrayMap<String, byte[]> mBlobs = new ArrayMap<>();
@Override
protected byte[] decryptSPBlob(String blobKeyName, byte[] blob, byte[] protectorSecret) {
if (mBlobs.containsKey(blobKeyName) && !Arrays.equals(mBlobs.get(blobKeyName), blob)) {
throw new AssertionFailedError("blobKeyName content is overwritten: " + blobKeyName);
protected byte[] decryptSpBlob(String protectorKeyAlias, byte[] blob, byte[] protectorSecret) {
if (mBlobs.containsKey(protectorKeyAlias) &&
!Arrays.equals(mBlobs.get(protectorKeyAlias), blob)) {
throw new AssertionFailedError("Blob was overwritten; protectorKeyAlias="
+ protectorKeyAlias);
}
ByteBuffer buffer = ByteBuffer.allocate(blob.length);
buffer.put(blob, 0, blob.length);
@@ -72,7 +74,7 @@ public class MockSyntheticPasswordManager extends SyntheticPasswordManager {
}
@Override
protected byte[] createSPBlob(String blobKeyName, byte[] data, byte[] protectorSecret,
protected byte[] createSpBlob(String protectorKeyAlias, byte[] data, byte[] protectorSecret,
long sid) {
ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES + data.length + Integer.BYTES
+ protectorSecret.length + Long.BYTES);
@@ -82,12 +84,12 @@ public class MockSyntheticPasswordManager extends SyntheticPasswordManager {
buffer.put(protectorSecret);
buffer.putLong(sid);
byte[] result = buffer.array();
mBlobs.put(blobKeyName, result);
mBlobs.put(protectorKeyAlias, result);
return result;
}
@Override
protected void destroySPBlobKey(String keyAlias) {
protected void destroyProtectorKey(String keyAlias) {
}
@Override