Merge "Use rootAlias to index chosen cert and its version." into pi-dev

This commit is contained in:
Dmitry Dementyev
2018-03-28 19:05:21 +00:00
committed by Android (Google) Code Review
9 changed files with 609 additions and 86 deletions

View File

@@ -19,10 +19,12 @@ package com.android.server.locksettings.recoverablekeystore;
import static android.security.keystore.recovery.KeyChainProtectionParams.TYPE_LOCKSCREEN;
import android.annotation.Nullable;
import android.annotation.NonNull;
import android.content.Context;
import android.security.keystore.recovery.KeyDerivationParams;
import android.security.keystore.recovery.KeyChainProtectionParams;
import android.security.keystore.recovery.KeyChainSnapshot;
import android.security.keystore.recovery.KeyDerivationParams;
import android.security.keystore.recovery.TrustedRootCertificates;
import android.security.keystore.recovery.WrappedApplicationKey;
import android.util.Log;
@@ -185,8 +187,12 @@ public class KeySyncTask implements Runnable {
}
PublicKey publicKey;
String rootCertAlias =
mRecoverableKeyStoreDb.getActiveRootOfTrust(mUserId, recoveryAgentUid);
rootCertAlias = replaceEmptyValueWithSecureDefault(rootCertAlias);
CertPath certPath = mRecoverableKeyStoreDb.getRecoveryServiceCertPath(mUserId,
recoveryAgentUid);
recoveryAgentUid, rootCertAlias);
if (certPath != null) {
Log.d(TAG, "Using the public key in stored CertPath for syncing");
publicKey = certPath.getCertificates().get(0).getPublicKey();
@@ -206,6 +212,14 @@ public class KeySyncTask implements Runnable {
return;
}
// The only place in this class which uses credential value
if (!TrustedRootCertificates.GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_ALIAS.equals(
rootCertAlias)) {
// TODO: allow only whitelisted LSKF usage
Log.w(TAG, "Untrusted root certificate is used by recovery agent "
+ recoveryAgentUid);
}
byte[] salt = generateSalt();
byte[] localLskfHash = hashCredentials(salt, mCredential);
@@ -225,6 +239,8 @@ public class KeySyncTask implements Runnable {
return;
}
// TODO: filter raw keys based on the root of trust.
// It is the only place in the class where raw key material is used.
SecretKey recoveryKey;
try {
recoveryKey = generateRecoveryKey();
@@ -451,4 +467,14 @@ public class KeySyncTask implements Runnable {
}
return keyEntries;
}
private @NonNull String replaceEmptyValueWithSecureDefault(
@Nullable String rootCertificateAlias) {
if (rootCertificateAlias == null || rootCertificateAlias.isEmpty()) {
Log.e(TAG, "rootCertificateAlias is null or empty");
// Use the default Google Key Vault Service CA certificate if the alias is not provided
rootCertificateAlias = TrustedRootCertificates.GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_ALIAS;
}
return rootCertificateAlias;
}
}

View File

@@ -176,6 +176,20 @@ public class RecoverableKeyStoreManager {
checkRecoverKeyStorePermission();
int userId = UserHandle.getCallingUserId();
int uid = Binder.getCallingUid();
rootCertificateAlias = replaceEmptyValueWithSecureDefault(rootCertificateAlias);
// Always set active alias to the argument of the last call to initRecoveryService method,
// even if cert file is incorrect.
String activeRootAlias = mDatabase.getActiveRootOfTrust(userId, uid);
if (activeRootAlias == null) {
Log.d(TAG, "Root of trust for recovery agent + " + uid
+ " is assigned for the first time to " + rootCertificateAlias);
mDatabase.setActiveRootOfTrust(userId, uid, rootCertificateAlias);
} else if (!activeRootAlias.equals(rootCertificateAlias)) {
Log.i(TAG, "Root of trust for recovery agent " + uid + " is changed to "
+ rootCertificateAlias + " from " + activeRootAlias);
mDatabase.setActiveRootOfTrust(userId, uid, rootCertificateAlias);
}
CertXml certXml;
try {
@@ -194,7 +208,7 @@ public class RecoverableKeyStoreManager {
// Check serial number
long newSerial = certXml.getSerial();
Long oldSerial = mDatabase.getRecoveryServiceCertSerial(userId, uid);
Long oldSerial = mDatabase.getRecoveryServiceCertSerial(userId, uid, rootCertificateAlias);
if (oldSerial != null && oldSerial >= newSerial) {
if (oldSerial == newSerial) {
Log.i(TAG, "The cert file serial number is the same, so skip updating.");
@@ -217,13 +231,16 @@ public class RecoverableKeyStoreManager {
ERROR_INVALID_CERTIFICATE, "Failed to validate certificate.");
}
boolean wasInitialized = mDatabase.getRecoveryServiceCertPath(userId, uid) != null;
boolean wasInitialized = mDatabase.getRecoveryServiceCertPath(userId, uid,
rootCertificateAlias) != null;
// Save the chosen and validated certificate into database
try {
Log.d(TAG, "Saving the randomly chosen endpoint certificate to database");
if (mDatabase.setRecoveryServiceCertPath(userId, uid, certPath) > 0) {
mDatabase.setRecoveryServiceCertSerial(userId, uid, newSerial);
if (mDatabase.setRecoveryServiceCertPath(userId, uid, rootCertificateAlias,
certPath) > 0) {
mDatabase.setRecoveryServiceCertSerial(userId, uid, rootCertificateAlias,
newSerial);
if (wasInitialized) {
Log.i(TAG, "This is a certificate change. Snapshot pending.");
mDatabase.setShouldCreateSnapshot(userId, uid, true);
@@ -253,9 +270,7 @@ public class RecoverableKeyStoreManager {
@NonNull byte[] recoveryServiceSigFile)
throws RemoteException {
checkRecoverKeyStorePermission();
if (rootCertificateAlias == null) {
Log.e(TAG, "rootCertificateAlias is null");
}
rootCertificateAlias = replaceEmptyValueWithSecureDefault(rootCertificateAlias);
Preconditions.checkNotNull(recoveryServiceCertFile, "recoveryServiceCertFile is null");
Preconditions.checkNotNull(recoveryServiceSigFile, "recoveryServiceSigFile is null");
@@ -509,9 +524,7 @@ public class RecoverableKeyStoreManager {
@NonNull List<KeyChainProtectionParams> secrets)
throws RemoteException {
checkRecoverKeyStorePermission();
if (rootCertificateAlias == null) {
Log.e(TAG, "rootCertificateAlias is null");
}
rootCertificateAlias = replaceEmptyValueWithSecureDefault(rootCertificateAlias);
Preconditions.checkNotNull(sessionId, "invalid session");
Preconditions.checkNotNull(verifierCertPath, "verifierCertPath is null");
Preconditions.checkNotNull(vaultParams, "vaultParams is null");
@@ -953,11 +966,7 @@ public class RecoverableKeyStoreManager {
}
private X509Certificate getRootCertificate(String rootCertificateAlias) throws RemoteException {
if (rootCertificateAlias == null || rootCertificateAlias.isEmpty()) {
// Use the default Google Key Vault Service CA certificate if the alias is not provided
rootCertificateAlias = TrustedRootCertificates.GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_ALIAS;
}
rootCertificateAlias = replaceEmptyValueWithSecureDefault(rootCertificateAlias);
X509Certificate rootCertificate =
TrustedRootCertificates.getRootCertificate(rootCertificateAlias);
if (rootCertificate == null) {
@@ -967,6 +976,16 @@ public class RecoverableKeyStoreManager {
return rootCertificate;
}
private @NonNull String replaceEmptyValueWithSecureDefault(
@Nullable String rootCertificateAlias) {
if (rootCertificateAlias == null || rootCertificateAlias.isEmpty()) {
Log.e(TAG, "rootCertificateAlias is null or empty");
// Use the default Google Key Vault Service CA certificate if the alias is not provided
rootCertificateAlias = TrustedRootCertificates.GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_ALIAS;
}
return rootCertificateAlias;
}
private void checkRecoverKeyStorePermission() {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.RECOVER_KEYSTORE,

View File

@@ -29,6 +29,7 @@ import android.util.Log;
import com.android.server.locksettings.recoverablekeystore.WrappedKey;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.KeysEntry;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.RecoveryServiceMetadataEntry;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.RootOfTrustEntry;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.UserMetadataEntry;
import java.io.ByteArrayInputStream;
@@ -385,13 +386,15 @@ public class RecoverableKeyStoreDb {
*
* @param userId The userId of the profile the application is running under.
* @param uid The uid of the application who initializes the local recovery components.
* @param rootAlias The root of trust alias.
* @return The value that were previously set, or null if there's none.
*
* @hide
*/
@Nullable
public Long getRecoveryServiceCertSerial(int userId, int uid) {
return getLong(userId, uid, RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_SERIAL);
public Long getRecoveryServiceCertSerial(int userId, int uid, @NonNull String rootAlias) {
return getLong(userId, uid, rootAlias,
RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_SERIAL);
}
/**
@@ -399,13 +402,16 @@ public class RecoverableKeyStoreDb {
*
* @param userId The userId of the profile the application is running under.
* @param uid The uid of the application who initializes the local recovery components.
* @param rootAlias The root of trust alias.
* @param serial The serial number contained in the XML file for recovery service certificates.
* @return The primary key of the inserted row, or -1 if failed.
*
* @hide
*/
public long setRecoveryServiceCertSerial(int userId, int uid, long serial) {
return setLong(userId, uid, RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_SERIAL, serial);
public long setRecoveryServiceCertSerial(int userId, int uid, @NonNull String rootAlias,
long serial) {
return setLong(userId, uid, rootAlias, RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_SERIAL,
serial);
}
/**
@@ -413,13 +419,15 @@ public class RecoverableKeyStoreDb {
*
* @param userId The userId of the profile the application is running under.
* @param uid The uid of the application who initializes the local recovery components.
* @param rootAlias The root of trust alias.
* @return The value that were previously set, or null if there's none.
*
* @hide
*/
@Nullable
public CertPath getRecoveryServiceCertPath(int userId, int uid) {
byte[] bytes = getBytes(userId, uid, RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_PATH);
public CertPath getRecoveryServiceCertPath(int userId, int uid, @NonNull String rootAlias) {
byte[] bytes = getBytes(userId, uid, rootAlias,
RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_PATH);
if (bytes == null) {
return null;
}
@@ -440,16 +448,17 @@ public class RecoverableKeyStoreDb {
*
* @param userId The userId of the profile the application is running under.
* @param uid The uid of the application who initializes the local recovery components.
* @param rootAlias The root of trust alias.
* @param certPath The certificate path of the recovery service.
* @return The primary key of the inserted row, or -1 if failed.
* @hide
*/
public long setRecoveryServiceCertPath(int userId, int uid, CertPath certPath) throws
CertificateEncodingException {
public long setRecoveryServiceCertPath(int userId, int uid, @NonNull String rootAlias,
CertPath certPath) throws CertificateEncodingException {
if (certPath.getCertificates().size() == 0) {
throw new CertificateEncodingException("No certificate contained in the cert path.");
}
return setBytes(userId, uid, RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_PATH,
return setBytes(userId, uid, rootAlias, RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_PATH,
certPath.getEncoded(CERT_PATH_ENCODING));
}
@@ -607,6 +616,85 @@ public class RecoverableKeyStoreDb {
}
}
/**
* Active root of trust for the recovery agent.
*
* @param userId The userId of the profile the application is running under.
* @param uid The uid of the application.
* @param rootAlias The root of trust alias.
* @return The primary key of the updated row, or -1 if failed.
*
* @hide
*/
public long setActiveRootOfTrust(int userId, int uid, @Nullable String rootAlias) {
SQLiteDatabase db = mKeyStoreDbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_ACTIVE_ROOT_OF_TRUST, rootAlias);
String selection =
RecoveryServiceMetadataEntry.COLUMN_NAME_USER_ID + " = ? AND "
+ RecoveryServiceMetadataEntry.COLUMN_NAME_UID + " = ?";
ensureRecoveryServiceMetadataEntryExists(userId, uid);
return db.update(RecoveryServiceMetadataEntry.TABLE_NAME, values,
selection, new String[] {String.valueOf(userId), String.valueOf(uid)});
}
/**
* Active root of trust for the recovery agent.
*
* @param userId The userId of the profile the application is running under.
* @param uid The uid of the application who initialized the local recovery components.
* @return Active root of trust alias of null if it was not set
*
* @hide
*/
public @Nullable String getActiveRootOfTrust(int userId, int uid) {
SQLiteDatabase db = mKeyStoreDbHelper.getReadableDatabase();
String[] projection = {
RecoveryServiceMetadataEntry._ID,
RecoveryServiceMetadataEntry.COLUMN_NAME_USER_ID,
RecoveryServiceMetadataEntry.COLUMN_NAME_UID,
RecoveryServiceMetadataEntry.COLUMN_NAME_ACTIVE_ROOT_OF_TRUST};
String selection =
RecoveryServiceMetadataEntry.COLUMN_NAME_USER_ID + " = ? AND "
+ RecoveryServiceMetadataEntry.COLUMN_NAME_UID + " = ?";
String[] selectionArguments = {Integer.toString(userId), Integer.toString(uid)};
try (
Cursor cursor = db.query(
RecoveryServiceMetadataEntry.TABLE_NAME,
projection,
selection,
selectionArguments,
/*groupBy=*/ null,
/*having=*/ null,
/*orderBy=*/ null)
) {
int count = cursor.getCount();
if (count == 0) {
return null;
}
if (count > 1) {
Log.wtf(TAG,
String.format(Locale.US,
"%d deviceId entries found for userId=%d uid=%d. "
+ "Should only ever be 0 or 1.", count, userId, uid));
return null;
}
cursor.moveToFirst();
int idx = cursor.getColumnIndexOrThrow(
RecoveryServiceMetadataEntry.COLUMN_NAME_ACTIVE_ROOT_OF_TRUST);
if (cursor.isNull(idx)) {
return null;
}
String result = cursor.getString(idx);
if (TextUtils.isEmpty(result)) {
return null;
}
return result;
}
}
/**
* Updates the counterId
*
@@ -874,7 +962,6 @@ public class RecoverableKeyStoreDb {
*
* @hide
*/
private long setBytes(int userId, int uid, String key, byte[] value) {
SQLiteDatabase db = mKeyStoreDbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
@@ -889,6 +976,176 @@ public class RecoverableKeyStoreDb {
RecoveryServiceMetadataEntry.TABLE_NAME, values, selection, selectionArguments);
}
/**
* Returns given binary value from the database.
*
* @param userId The userId of the profile the application is running under.
* @param uid The uid of the application who initialized the local recovery components.
* @param rootAlias The root of trust alias.
* @param key from {@code RootOfTrustEntry}
* @return The value that were previously set, or null if there's none.
*
* @hide
*/
private byte[] getBytes(int userId, int uid, String rootAlias, String key) {
SQLiteDatabase db = mKeyStoreDbHelper.getReadableDatabase();
String[] projection = {
RootOfTrustEntry._ID,
RootOfTrustEntry.COLUMN_NAME_USER_ID,
RootOfTrustEntry.COLUMN_NAME_UID,
RootOfTrustEntry.COLUMN_NAME_ROOT_ALIAS,
key};
String selection =
RootOfTrustEntry.COLUMN_NAME_USER_ID + " = ? AND "
+ RootOfTrustEntry.COLUMN_NAME_UID + " = ? AND "
+ RootOfTrustEntry.COLUMN_NAME_ROOT_ALIAS + " = ?";
String[] selectionArguments = {Integer.toString(userId), Integer.toString(uid), rootAlias};
try (
Cursor cursor = db.query(
RootOfTrustEntry.TABLE_NAME,
projection,
selection,
selectionArguments,
/*groupBy=*/ null,
/*having=*/ null,
/*orderBy=*/ null)
) {
int count = cursor.getCount();
if (count == 0) {
return null;
}
if (count > 1) {
Log.wtf(TAG,
String.format(Locale.US,
"%d entries found for userId=%d uid=%d. "
+ "Should only ever be 0 or 1.", count, userId, uid));
return null;
}
cursor.moveToFirst();
int idx = cursor.getColumnIndexOrThrow(key);
if (cursor.isNull(idx)) {
return null;
} else {
return cursor.getBlob(idx);
}
}
}
/**
* Sets a binary value in the database.
*
* @param userId The userId of the profile the application is running under.
* @param uid The uid of the application who initialized the local recovery components.
* @param rootAlias The root of trust alias.
* @param key defined in {@code RootOfTrustEntry}
* @param value new value.
* @return The primary key of the inserted row, or -1 if failed.
*
* @hide
*/
private long setBytes(int userId, int uid, String rootAlias, String key, byte[] value) {
SQLiteDatabase db = mKeyStoreDbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(key, value);
String selection =
RootOfTrustEntry.COLUMN_NAME_USER_ID + " = ? AND "
+ RootOfTrustEntry.COLUMN_NAME_UID + " = ? AND "
+ RootOfTrustEntry.COLUMN_NAME_ROOT_ALIAS + " = ?";
String[] selectionArguments = {Integer.toString(userId), Integer.toString(uid), rootAlias};
ensureRootOfTrustEntryExists(userId, uid, rootAlias);
return db.update(
RootOfTrustEntry.TABLE_NAME, values, selection, selectionArguments);
}
/**
* Returns given long value from the database.
*
* @param userId The userId of the profile the application is running under.
* @param uid The uid of the application who initialized the local recovery components.
* @param rootAlias The root of trust alias.
* @param key from {@code RootOfTrustEntry}
* @return The value that were previously set, or null if there's none.
*
* @hide
*/
private Long getLong(int userId, int uid, String rootAlias, String key) {
SQLiteDatabase db = mKeyStoreDbHelper.getReadableDatabase();
String[] projection = {
RootOfTrustEntry._ID,
RootOfTrustEntry.COLUMN_NAME_USER_ID,
RootOfTrustEntry.COLUMN_NAME_UID,
RootOfTrustEntry.COLUMN_NAME_ROOT_ALIAS,
key};
String selection =
RootOfTrustEntry.COLUMN_NAME_USER_ID + " = ? AND "
+ RootOfTrustEntry.COLUMN_NAME_UID + " = ? AND "
+ RootOfTrustEntry.COLUMN_NAME_ROOT_ALIAS + " = ?";
String[] selectionArguments = {Integer.toString(userId), Integer.toString(uid), rootAlias};
try (
Cursor cursor = db.query(
RootOfTrustEntry.TABLE_NAME,
projection,
selection,
selectionArguments,
/*groupBy=*/ null,
/*having=*/ null,
/*orderBy=*/ null)
) {
int count = cursor.getCount();
if (count == 0) {
return null;
}
if (count > 1) {
Log.wtf(TAG,
String.format(Locale.US,
"%d entries found for userId=%d uid=%d. "
+ "Should only ever be 0 or 1.", count, userId, uid));
return null;
}
cursor.moveToFirst();
int idx = cursor.getColumnIndexOrThrow(key);
if (cursor.isNull(idx)) {
return null;
} else {
return cursor.getLong(idx);
}
}
}
/**
* Sets a long value in the database.
*
* @param userId The userId of the profile the application is running under.
* @param uid The uid of the application who initialized the local recovery components.
* @param rootAlias The root of trust alias.
* @param key defined in {@code RootOfTrustEntry}
* @param value new value.
* @return The primary key of the inserted row, or -1 if failed.
*
* @hide
*/
private long setLong(int userId, int uid, String rootAlias, String key, long value) {
SQLiteDatabase db = mKeyStoreDbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(key, value);
String selection =
RootOfTrustEntry.COLUMN_NAME_USER_ID + " = ? AND "
+ RootOfTrustEntry.COLUMN_NAME_UID + " = ? AND "
+ RootOfTrustEntry.COLUMN_NAME_ROOT_ALIAS + " = ?";
String[] selectionArguments = {Integer.toString(userId), Integer.toString(uid), rootAlias};
ensureRootOfTrustEntryExists(userId, uid, rootAlias);
return db.update(
RootOfTrustEntry.TABLE_NAME, values, selection, selectionArguments);
}
/**
* Creates an empty row in the recovery service metadata table if such a row doesn't exist for
* the given userId and uid, so db.update will succeed.
@@ -902,6 +1159,20 @@ public class RecoverableKeyStoreDb {
values, SQLiteDatabase.CONFLICT_IGNORE);
}
/**
* Creates an empty row in the root of trust table if such a row doesn't exist for
* the given userId and uid, so db.update will succeed.
*/
private void ensureRootOfTrustEntryExists(int userId, int uid, String rootAlias) {
SQLiteDatabase db = mKeyStoreDbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(RootOfTrustEntry.COLUMN_NAME_USER_ID, userId);
values.put(RootOfTrustEntry.COLUMN_NAME_UID, uid);
values.put(RootOfTrustEntry.COLUMN_NAME_ROOT_ALIAS, rootAlias);
db.insertWithOnConflict(RootOfTrustEntry.TABLE_NAME, /*nullColumnHack=*/ null,
values, SQLiteDatabase.CONFLICT_IGNORE);
}
/**
* Closes all open connections to the database.
*/

View File

@@ -230,16 +230,19 @@ class RecoverableKeyStoreDbContract {
/**
* The public key of the recovery service.
* Deprecated.
*/
static final String COLUMN_NAME_PUBLIC_KEY = "public_key";
/**
* The certificate path of the recovery service.
* Deprecated.
*/
static final String COLUMN_NAME_CERT_PATH = "cert_path";
/**
* The serial number contained in the certificate XML file of the recovery service.
* Deprecated.
*/
static final String COLUMN_NAME_CERT_SERIAL = "cert_serial";
@@ -257,5 +260,42 @@ class RecoverableKeyStoreDbContract {
* The server parameters of the recovery service.
*/
static final String COLUMN_NAME_SERVER_PARAMS = "server_params";
/**
* Active root of trust
*/
static final String COLUMN_NAME_ACTIVE_ROOT_OF_TRUST = "active_root_of_trust";
}
/**
* Table data for given recovery agent and root of trust pair.
*/
static class RootOfTrustEntry implements BaseColumns {
static final String TABLE_NAME = "root_of_trust";
/**
* The user id of the profile the application is running under.
*/
static final String COLUMN_NAME_USER_ID = "user_id";
/**
* The uid of the application that initializes the local recovery components.
*/
static final String COLUMN_NAME_UID = "uid";
/**
* Root of trust alias
*/
static final String COLUMN_NAME_ROOT_ALIAS = "root_alias";
/**
* The certificate path of the recovery service.
*/
static final String COLUMN_NAME_CERT_PATH = "cert_path";
/**
* The serial number contained in the certificate XML file of the recovery service.
*/
static final String COLUMN_NAME_CERT_SERIAL = "cert_serial";
}
}

View File

@@ -23,6 +23,7 @@ import android.util.Log;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.KeysEntry;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.RecoveryServiceMetadataEntry;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.RootOfTrustEntry;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.UserMetadataEntry;
/**
@@ -31,7 +32,7 @@ import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKe
class RecoverableKeyStoreDbHelper extends SQLiteOpenHelper {
private static final String TAG = "RecoverableKeyStoreDbHp";
static final int DATABASE_VERSION = 3;
static final int DATABASE_VERSION = 4;
private static final String DATABASE_NAME = "recoverablekeystore.db";
private static final String SQL_CREATE_KEYS_ENTRY =
@@ -61,6 +62,7 @@ class RecoverableKeyStoreDbHelper extends SQLiteOpenHelper {
+ RecoveryServiceMetadataEntry.COLUMN_NAME_UID + " INTEGER,"
+ RecoveryServiceMetadataEntry.COLUMN_NAME_SNAPSHOT_VERSION + " INTEGER,"
+ RecoveryServiceMetadataEntry.COLUMN_NAME_SHOULD_CREATE_SNAPSHOT + " INTEGER,"
+ RecoveryServiceMetadataEntry.COLUMN_NAME_ACTIVE_ROOT_OF_TRUST + " TEXT,"
+ RecoveryServiceMetadataEntry.COLUMN_NAME_PUBLIC_KEY + " BLOB,"
+ RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_PATH + " BLOB,"
+ RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_SERIAL + " INTEGER,"
@@ -71,6 +73,19 @@ class RecoverableKeyStoreDbHelper extends SQLiteOpenHelper {
+ RecoveryServiceMetadataEntry.COLUMN_NAME_USER_ID + ","
+ RecoveryServiceMetadataEntry.COLUMN_NAME_UID + "))";
private static final String SQL_CREATE_ROOT_OF_TRUST_ENTRY =
"CREATE TABLE " + RootOfTrustEntry.TABLE_NAME + " ("
+ RootOfTrustEntry._ID + " INTEGER PRIMARY KEY,"
+ RootOfTrustEntry.COLUMN_NAME_USER_ID + " INTEGER,"
+ RootOfTrustEntry.COLUMN_NAME_UID + " INTEGER,"
+ RootOfTrustEntry.COLUMN_NAME_ROOT_ALIAS + " TEST,"
+ RootOfTrustEntry.COLUMN_NAME_CERT_PATH + " BLOB,"
+ RootOfTrustEntry.COLUMN_NAME_CERT_SERIAL + " INTEGER,"
+ "UNIQUE("
+ RootOfTrustEntry.COLUMN_NAME_USER_ID + ","
+ RootOfTrustEntry.COLUMN_NAME_UID + ","
+ RootOfTrustEntry.COLUMN_NAME_ROOT_ALIAS + "))";
private static final String SQL_DELETE_KEYS_ENTRY =
"DROP TABLE IF EXISTS " + KeysEntry.TABLE_NAME;
@@ -80,6 +95,9 @@ class RecoverableKeyStoreDbHelper extends SQLiteOpenHelper {
private static final String SQL_DELETE_RECOVERY_SERVICE_METADATA_ENTRY =
"DROP TABLE IF EXISTS " + RecoveryServiceMetadataEntry.TABLE_NAME;
private static final String SQL_DELETE_ROOT_OF_TRUST_ENTRY =
"DROP TABLE IF EXISTS " + RootOfTrustEntry.TABLE_NAME;
RecoverableKeyStoreDbHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@@ -89,21 +107,44 @@ class RecoverableKeyStoreDbHelper extends SQLiteOpenHelper {
db.execSQL(SQL_CREATE_KEYS_ENTRY);
db.execSQL(SQL_CREATE_USER_METADATA_ENTRY);
db.execSQL(SQL_CREATE_RECOVERY_SERVICE_METADATA_ENTRY);
db.execSQL(SQL_CREATE_ROOT_OF_TRUST_ENTRY);
}
@Override
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.e(TAG, "Recreating recoverablekeystore after unexpected version downgrade.");
dropAllKnownTables(db); // Wipe database.
onCreate(db);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (oldVersion < 2) {
db.execSQL(SQL_DELETE_KEYS_ENTRY);
db.execSQL(SQL_DELETE_USER_METADATA_ENTRY);
db.execSQL(SQL_DELETE_RECOVERY_SERVICE_METADATA_ENTRY);
dropAllKnownTables(db); // Wipe database.
onCreate(db);
return;
}
if (oldVersion < 3) {
if (oldVersion < 3 && newVersion >= 3) {
upgradeDbForVersion3(db);
oldVersion = 3;
}
if (oldVersion < 4 && newVersion >= 4) {
upgradeDbForVersion4(db);
oldVersion = 4;
}
if (oldVersion != newVersion) {
Log.e(TAG, "Failed to update recoverablekeystore database to the most recent version");
}
}
private void dropAllKnownTables(SQLiteDatabase db) {
db.execSQL(SQL_DELETE_KEYS_ENTRY);
db.execSQL(SQL_DELETE_USER_METADATA_ENTRY);
db.execSQL(SQL_DELETE_RECOVERY_SERVICE_METADATA_ENTRY);
db.execSQL(SQL_DELETE_ROOT_OF_TRUST_ENTRY);
}
private void upgradeDbForVersion3(SQLiteDatabase db) {
@@ -115,6 +156,16 @@ class RecoverableKeyStoreDbHelper extends SQLiteOpenHelper {
null);
}
private void upgradeDbForVersion4(SQLiteDatabase db) {
Log.d(TAG, "Updating recoverable keystore database to version 4");
// Add new table with two columns for cert path and cert serial number.
db.execSQL(SQL_CREATE_ROOT_OF_TRUST_ENTRY);
// adds column to store root of trust currently used by the recovery agent
addColumnToTable(db, RecoveryServiceMetadataEntry.TABLE_NAME,
RecoveryServiceMetadataEntry.COLUMN_NAME_ACTIVE_ROOT_OF_TRUST, "TEXT",
/*defaultStr=*/ null);
}
private static void addColumnToTable(
SQLiteDatabase db, String tableName, String column, String columnType,
String defaultStr) {

View File

@@ -74,6 +74,7 @@ import javax.crypto.SecretKey;
public class KeySyncTaskTest {
private static final String KEY_ALGORITHM = "AES";
private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
private static final String TEST_ROOT_CERT_ALIAS = "trusted_root";
private static final String WRAPPING_KEY_ALIAS = "KeySyncTaskTest/WrappingKey";
private static final String DATABASE_FILE_NAME = "recoverablekeystore.db";
private static final int TEST_USER_ID = 1000;
@@ -111,6 +112,11 @@ public class KeySyncTaskTest {
new int[] {TYPE_LOCKSCREEN});
mRecoverableKeyStoreDb.setRecoverySecretTypes(TEST_USER_ID, TEST_RECOVERY_AGENT_UID2,
new int[] {TYPE_LOCKSCREEN});
mRecoverableKeyStoreDb.setActiveRootOfTrust(TEST_USER_ID, TEST_RECOVERY_AGENT_UID,
TEST_ROOT_CERT_ALIAS);
mRecoverableKeyStoreDb.setActiveRootOfTrust(TEST_USER_ID, TEST_RECOVERY_AGENT_UID2,
TEST_ROOT_CERT_ALIAS);
mRecoverySnapshotStorage = new RecoverySnapshotStorage();
mKeySyncTask = new KeySyncTask(
@@ -247,7 +253,7 @@ public class KeySyncTaskTest {
TEST_APP_KEY_ALIAS,
WrappedKey.fromSecretKey(mEncryptKey, applicationKey));
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
mKeySyncTask.run();
@@ -263,7 +269,7 @@ public class KeySyncTaskTest {
mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID);
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
mKeySyncTask.run();
@@ -273,7 +279,7 @@ public class KeySyncTaskTest {
@Test
public void run_sendsEncryptedKeysIfAvailableToSync_withRawPublicKey() throws Exception {
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
mRecoverableKeyStoreDb.setServerParams(
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
@@ -320,7 +326,7 @@ public class KeySyncTaskTest {
@Test
public void run_sendsEncryptedKeysIfAvailableToSync_withCertPath() throws Exception {
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
mRecoverableKeyStoreDb.setServerParams(
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
@@ -339,7 +345,7 @@ public class KeySyncTaskTest {
@Test
public void run_setsCorrectSnapshotVersion() throws Exception {
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
@@ -358,7 +364,7 @@ public class KeySyncTaskTest {
@Test
public void run_recreatesMissingSnapshot() throws Exception {
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
@@ -388,7 +394,7 @@ public class KeySyncTaskTest {
mPlatformKeyManager);
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
SecretKey applicationKey =
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
@@ -414,7 +420,7 @@ public class KeySyncTaskTest {
mPlatformKeyManager);
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
SecretKey applicationKey =
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
@@ -441,7 +447,7 @@ public class KeySyncTaskTest {
mPlatformKeyManager);
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
SecretKey applicationKey =
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
@@ -457,9 +463,9 @@ public class KeySyncTaskTest {
@Test
public void run_sendsEncryptedKeysWithTwoRegisteredAgents() throws Exception {
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, TestData.CERT_PATH_1);
TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID2)).thenReturn(true);
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
@@ -479,9 +485,9 @@ public class KeySyncTaskTest {
new int[] {1000});
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, TestData.CERT_PATH_1);
TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID2)).thenReturn(true);
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
@@ -496,9 +502,9 @@ public class KeySyncTaskTest {
@Test
public void run_notifiesNonregisteredAgent() throws Exception {
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, TestData.CERT_PATH_1);
TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID2)).thenReturn(false);
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);

View File

@@ -44,9 +44,10 @@ import android.security.keystore.AndroidKeyStoreProvider;
import android.security.keystore.AndroidKeyStoreSecretKey;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.security.keystore.recovery.KeyDerivationParams;
import android.security.keystore.recovery.KeyChainProtectionParams;
import android.security.keystore.recovery.KeyDerivationParams;
import android.security.keystore.recovery.RecoveryCertPath;
import android.security.keystore.recovery.TrustedRootCertificates;
import android.security.keystore.recovery.WrappedApplicationKey;
import android.support.test.filters.SmallTest;
import android.support.test.InstrumentationRegistry;
@@ -90,6 +91,8 @@ public class RecoverableKeyStoreManagerTest {
private static final String DATABASE_FILE_NAME = "recoverablekeystore.db";
private static final String ROOT_CERTIFICATE_ALIAS = "";
private static final String DEFAULT_ROOT_CERT_ALIAS =
TrustedRootCertificates.GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_ALIAS;
private static final String TEST_SESSION_ID = "karlin";
private static final byte[] TEST_PUBLIC_KEY = new byte[] {
(byte) 0x30, (byte) 0x59, (byte) 0x30, (byte) 0x13, (byte) 0x06, (byte) 0x07, (byte) 0x2a,
@@ -143,7 +146,7 @@ public class RecoverableKeyStoreManagerTest {
private static final String KEY_ALGORITHM = "AES";
private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
private static final String WRAPPING_KEY_ALIAS = "RecoverableKeyStoreManagerTest/WrappingKey";
private static final String TEST_ROOT_CERT_ALIAS = "";
private static final String TEST_DEFAULT_ROOT_CERT_ALIAS = "";
private static final KeyChainProtectionParams TEST_PROTECTION_PARAMS =
new KeyChainProtectionParams.Builder()
.setUserSecretType(TYPE_LOCKSCREEN)
@@ -299,10 +302,10 @@ public class RecoverableKeyStoreManagerTest {
TestData.getCertXmlWithSerial(certSerial));
assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse();
assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid)).isEqualTo(
TestData.CERT_PATH_1);
assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid)).isEqualTo(
certSerial);
assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid,
DEFAULT_ROOT_CERT_ALIAS)).isEqualTo(TestData.CERT_PATH_1);
assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid,
DEFAULT_ROOT_CERT_ALIAS)).isEqualTo(certSerial);
assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid)).isNull();
}
@@ -328,7 +331,8 @@ public class RecoverableKeyStoreManagerTest {
byte[] modifiedCertXml = TestData.getCertXml();
modifiedCertXml[modifiedCertXml.length - 50] ^= 1; // Flip a bit in the certificate
try {
mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS, modifiedCertXml);
mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
modifiedCertXml);
fail("should have thrown");
} catch (ServiceSpecificException e) {
assertThat(e.getMessage()).contains("validate cert");
@@ -346,8 +350,8 @@ public class RecoverableKeyStoreManagerTest {
mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
TestData.getCertXmlWithSerial(certSerial + 1));
assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid))
.isEqualTo(certSerial + 1);
assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid,
DEFAULT_ROOT_CERT_ALIAS)).isEqualTo(certSerial + 1);
assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
}
@@ -362,8 +366,8 @@ public class RecoverableKeyStoreManagerTest {
mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
TestData.getCertXmlWithSerial(certSerial - 1));
assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid))
.isEqualTo(certSerial);
assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid,
DEFAULT_ROOT_CERT_ALIAS)).isEqualTo(certSerial);
assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse();
}
@@ -391,8 +395,10 @@ public class RecoverableKeyStoreManagerTest {
mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS, TEST_PUBLIC_KEY);
assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid)).isNull();
assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid)).isNull();
assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid,
DEFAULT_ROOT_CERT_ALIAS)).isNull();
assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid,
DEFAULT_ROOT_CERT_ALIAS)).isNull();
assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid)).isNotNull();
}
@@ -406,8 +412,8 @@ public class RecoverableKeyStoreManagerTest {
ROOT_CERTIFICATE_ALIAS, TestData.getCertXml(), TestData.getSigXml());
assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse();
assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid)).isEqualTo(
TestData.CERT_PATH_1);
assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid,
DEFAULT_ROOT_CERT_ALIAS)).isEqualTo(TestData.CERT_PATH_1);
assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid)).isNull();
}
@@ -480,7 +486,7 @@ public class RecoverableKeyStoreManagerTest {
public void startRecoverySessionWithCertPath_storesTheSessionInfo() throws Exception {
mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
TEST_SESSION_ID,
TEST_ROOT_CERT_ALIAS,
TEST_DEFAULT_ROOT_CERT_ALIAS,
RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1),
TEST_VAULT_PARAMS,
TEST_VAULT_CHALLENGE,
@@ -497,7 +503,7 @@ public class RecoverableKeyStoreManagerTest {
public void startRecoverySessionWithCertPath_checksPermissionFirst() throws Exception {
mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
TEST_SESSION_ID,
TEST_ROOT_CERT_ALIAS,
TEST_DEFAULT_ROOT_CERT_ALIAS,
RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1),
TEST_VAULT_PARAMS,
TEST_VAULT_CHALLENGE,
@@ -601,7 +607,7 @@ public class RecoverableKeyStoreManagerTest {
try {
mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
TEST_SESSION_ID,
TEST_ROOT_CERT_ALIAS,
TEST_DEFAULT_ROOT_CERT_ALIAS,
RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1),
TEST_VAULT_PARAMS,
TEST_VAULT_CHALLENGE,
@@ -620,7 +626,7 @@ public class RecoverableKeyStoreManagerTest {
try {
mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
TEST_SESSION_ID,
TEST_ROOT_CERT_ALIAS,
TEST_DEFAULT_ROOT_CERT_ALIAS,
RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1),
vaultParams,
TEST_VAULT_CHALLENGE,
@@ -638,7 +644,7 @@ public class RecoverableKeyStoreManagerTest {
try {
mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
TEST_SESSION_ID,
TEST_ROOT_CERT_ALIAS,
TEST_DEFAULT_ROOT_CERT_ALIAS,
RecoveryCertPath.createRecoveryCertPath(emptyCertPath),
TEST_VAULT_PARAMS,
TEST_VAULT_CHALLENGE,
@@ -658,7 +664,7 @@ public class RecoverableKeyStoreManagerTest {
try {
mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
TEST_SESSION_ID,
TEST_ROOT_CERT_ALIAS,
TEST_DEFAULT_ROOT_CERT_ALIAS,
RecoveryCertPath.createRecoveryCertPath(shortCertPath),
TEST_VAULT_PARAMS,
TEST_VAULT_CHALLENGE,

View File

@@ -34,6 +34,7 @@ import android.support.test.runner.AndroidJUnit4;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.KeysEntry;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.RecoveryServiceMetadataEntry;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.RootOfTrustEntry;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.UserMetadataEntry;
@SmallTest
@@ -55,6 +56,7 @@ public class RecoverableKeyStoreDbHelperTest {
private static final String TEST_SECRET_TYPES = "test-secret-types";
private static final long TEST_COUNTER_ID = -3981205205038476415L;
private static final byte[] TEST_SERVER_PARAMS = "test-server-params".getBytes(UTF_8);
private static final String TEST_ROOT_ALIAS = "root_cert_alias";
private static final byte[] TEST_CERT_PATH = "test-cert-path".getBytes(UTF_8);
private static final long TEST_CERT_SERIAL = 1000L;
@@ -135,6 +137,32 @@ public class RecoverableKeyStoreDbHelperTest {
checkAllColumns();
}
@Test
public void onUpgrade_v2_to_v3_to_v4() throws Exception {
createV2Tables();
assertThat(isRootOfTrustTableAvailable()).isFalse(); // V2 doesn't have the table;
mDatabaseHelper.onUpgrade(mDatabase, /*oldVersion=*/ 2, /*newVersion=*/ 3);
assertThat(isRootOfTrustTableAvailable()).isFalse(); // V3 doesn't have the table;
mDatabaseHelper.onUpgrade(mDatabase, /*oldVersion=*/ 3,
RecoverableKeyStoreDbHelper.DATABASE_VERSION);
checkAllColumns();
}
private boolean isRootOfTrustTableAvailable() {
ContentValues values = new ContentValues();
values.put(RootOfTrustEntry.COLUMN_NAME_USER_ID, TEST_USER_ID);
values.put(RootOfTrustEntry.COLUMN_NAME_UID, TEST_UID);
values.put(RootOfTrustEntry.COLUMN_NAME_ROOT_ALIAS, TEST_ROOT_ALIAS);
values.put(RootOfTrustEntry.COLUMN_NAME_CERT_PATH, TEST_CERT_PATH);
values.put(RootOfTrustEntry.COLUMN_NAME_CERT_SERIAL, TEST_CERT_SERIAL);
return mDatabase.insert(RootOfTrustEntry.TABLE_NAME, /*nullColumnHack=*/ null, values)
> -1;
}
private void checkAllColumns() throws Exception {
// Check the table containing encrypted application keys
ContentValues values = new ContentValues();
@@ -165,6 +193,7 @@ public class RecoverableKeyStoreDbHelperTest {
TEST_SNAPSHOT_VERSION);
values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_SHOULD_CREATE_SNAPSHOT,
TEST_SHOULD_CREATE_SNAPSHOT);
values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_ACTIVE_ROOT_OF_TRUST, TEST_ROOT_ALIAS);
values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_PUBLIC_KEY, TEST_PUBLIC_KEY);
values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_SECRET_TYPES, TEST_SECRET_TYPES);
values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_COUNTER_ID, TEST_COUNTER_ID);
@@ -175,5 +204,8 @@ public class RecoverableKeyStoreDbHelperTest {
mDatabase.insert(RecoveryServiceMetadataEntry.TABLE_NAME, /*nullColumnHack=*/ null,
values))
.isGreaterThan(-1L);
// Check the table about recovery service and root of trust data introduced in V4
assertThat(isRootOfTrustTableAvailable()).isTrue();
}
}

View File

@@ -49,6 +49,9 @@ import java.util.Map;
public class RecoverableKeyStoreDbTest {
private static final String DATABASE_FILE_NAME = "recoverablekeystore.db";
private static final String TEST_ROOT_CERT_ALIAS = "trusted_root";
private static final String TEST_ROOT_CERT_ALIAS2 = "another_trusted_root";
private RecoverableKeyStoreDb mRecoverableKeyStoreDb;
private File mDatabaseFile;
@@ -284,7 +287,8 @@ public class RecoverableKeyStoreDbTest {
Map<String, Integer> statuses = mRecoverableKeyStoreDb.getStatusForAllKeys(uid);
assertThat(statuses).hasSize(3);
assertThat(statuses).containsEntry(alias, RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS);
assertThat(statuses).containsEntry(alias,
RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS);
assertThat(statuses).containsEntry(alias2, status);
assertThat(statuses).containsEntry(alias3, status);
@@ -401,26 +405,53 @@ public class RecoverableKeyStoreDbTest {
public void setRecoveryServiceCertPath_replaceOldValue() throws Exception {
int userId = 12;
int uid = 10009;
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(userId, uid, TestData.CERT_PATH_1);
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(userId, uid, TestData.CERT_PATH_2);
assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid)).isEqualTo(
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(userId, uid, TEST_ROOT_CERT_ALIAS,
TestData.CERT_PATH_1);
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(userId, uid, TEST_ROOT_CERT_ALIAS,
TestData.CERT_PATH_2);
assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid,
TEST_ROOT_CERT_ALIAS)).isEqualTo(TestData.CERT_PATH_2);
}
@Test
public void setRecoveryServiceCertPath_updateValuesForCorrectRootCert() throws Exception {
int userId = 12;
int uid = 10009;
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(userId, uid, TEST_ROOT_CERT_ALIAS,
TestData.CERT_PATH_1);
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(userId, uid, TEST_ROOT_CERT_ALIAS2,
TestData.CERT_PATH_1);
assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid,
TEST_ROOT_CERT_ALIAS)).isEqualTo(TestData.CERT_PATH_1);
assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid,
TEST_ROOT_CERT_ALIAS2)).isEqualTo(TestData.CERT_PATH_1);
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(userId, uid, TEST_ROOT_CERT_ALIAS2,
TestData.CERT_PATH_2);
assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid,
TEST_ROOT_CERT_ALIAS)).isEqualTo(TestData.CERT_PATH_1);
assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid,
TEST_ROOT_CERT_ALIAS2)).isEqualTo(TestData.CERT_PATH_2);
}
@Test
public void getRecoveryServiceCertPath_returnsNullIfNoValue() throws Exception {
int userId = 12;
int uid = 10009;
assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid)).isNull();
assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid,
TEST_ROOT_CERT_ALIAS)).isNull();
}
@Test
public void getRecoveryServiceCertPath_returnsInsertedValue() throws Exception {
int userId = 12;
int uid = 10009;
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(userId, uid, TestData.CERT_PATH_1);
assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid)).isEqualTo(
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(userId, uid, TEST_ROOT_CERT_ALIAS,
TestData.CERT_PATH_1);
assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid,
TEST_ROOT_CERT_ALIAS)).isEqualTo(TestData.CERT_PATH_1);
}
@Test
@@ -428,25 +459,50 @@ public class RecoverableKeyStoreDbTest {
int userId = 12;
int uid = 10009;
mRecoverableKeyStoreDb.setRecoveryServiceCertSerial(userId, uid, 1L);
mRecoverableKeyStoreDb.setRecoveryServiceCertSerial(userId, uid, 3L);
assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid)).isEqualTo(3L);
mRecoverableKeyStoreDb.setRecoveryServiceCertSerial(userId, uid, TEST_ROOT_CERT_ALIAS, 1L);
mRecoverableKeyStoreDb.setRecoveryServiceCertSerial(userId, uid, TEST_ROOT_CERT_ALIAS, 3L);
assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid,
TEST_ROOT_CERT_ALIAS)).isEqualTo(3L);
}
@Test
public void setRecoveryServiceCertSerial_updateValuesForCorrectRootCert() throws Exception {
int userId = 12;
int uid = 10009;
mRecoverableKeyStoreDb.setRecoveryServiceCertSerial(userId, uid, TEST_ROOT_CERT_ALIAS, 1L);
mRecoverableKeyStoreDb.setRecoveryServiceCertSerial(userId, uid, TEST_ROOT_CERT_ALIAS2, 1L);
assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid,
TEST_ROOT_CERT_ALIAS)).isEqualTo(1L);
assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid,
TEST_ROOT_CERT_ALIAS2)).isEqualTo(1L);
mRecoverableKeyStoreDb.setRecoveryServiceCertSerial(userId, uid, TEST_ROOT_CERT_ALIAS2, 3L);
assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid,
TEST_ROOT_CERT_ALIAS)).isEqualTo(1L);
assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid,
TEST_ROOT_CERT_ALIAS2)).isEqualTo(3L);
}
@Test
public void getRecoveryServiceCertSerial_returnsNullIfNoValue() throws Exception {
int userId = 12;
int uid = 10009;
assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid)).isNull();
assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid,
TEST_ROOT_CERT_ALIAS)).isNull();
assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid,
TEST_ROOT_CERT_ALIAS2)).isNull();
}
@Test
public void getRecoveryServiceCertSerial_returnsInsertedValue() throws Exception {
int userId = 12;
int uid = 10009;
mRecoverableKeyStoreDb.setRecoveryServiceCertSerial(userId, uid, 1234L);
assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid)).isEqualTo(
1234L);
mRecoverableKeyStoreDb.setRecoveryServiceCertSerial(userId, uid,
TEST_ROOT_CERT_ALIAS, 1234L);
assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid,
TEST_ROOT_CERT_ALIAS)).isEqualTo(1234L);
}
@Test
@@ -479,6 +535,24 @@ public class RecoverableKeyStoreDbTest {
assertThat(agents).contains(uid2);
}
@Test
public void setActiveRootOfTrust_emptyDefaultValue() throws Exception {
int userId = 12;
int uid = 10009;
assertThat(mRecoverableKeyStoreDb.getActiveRootOfTrust(userId, uid)).isEqualTo(null);
}
@Test
public void setActiveRootOfTrust_updateValue() throws Exception {
int userId = 12;
int uid = 10009;
mRecoverableKeyStoreDb.setActiveRootOfTrust(userId, uid, "root");
assertThat(mRecoverableKeyStoreDb.getActiveRootOfTrust(userId, uid)).isEqualTo("root");
mRecoverableKeyStoreDb.setActiveRootOfTrust(userId, uid, "root2");
assertThat(mRecoverableKeyStoreDb.getActiveRootOfTrust(userId, uid)).isEqualTo("root2");
}
@Test
public void setRecoverySecretTypes_emptyDefaultValue() throws Exception {
int userId = 12;
@@ -495,11 +569,9 @@ public class RecoverableKeyStoreDbTest {
int[] types2 = new int[]{2};
mRecoverableKeyStoreDb.setRecoverySecretTypes(userId, uid, types1);
assertThat(mRecoverableKeyStoreDb.getRecoverySecretTypes(userId, uid)).isEqualTo(
types1);
assertThat(mRecoverableKeyStoreDb.getRecoverySecretTypes(userId, uid)).isEqualTo(types1);
mRecoverableKeyStoreDb.setRecoverySecretTypes(userId, uid, types2);
assertThat(mRecoverableKeyStoreDb.getRecoverySecretTypes(userId, uid)).isEqualTo(
types2);
assertThat(mRecoverableKeyStoreDb.getRecoverySecretTypes(userId, uid)).isEqualTo(types2);
}
@Test