private final int mCredentialType;
private final String mCredential;
private final PlatformKeyManager.Factory mPlatformKeyManagerFactory;
- private final VaultKeySupplier mVaultKeySupplier;
private final RecoverySnapshotStorage mRecoverySnapshotStorage;
private final RecoverySnapshotListenersStorage mSnapshotListenersStorage;
userId,
credentialType,
credential,
- () -> PlatformKeyManager.getInstance(context, recoverableKeyStoreDb, userId),
- () -> {
- throw new UnsupportedOperationException("Not implemented vault key.");
- });
+ () -> PlatformKeyManager.getInstance(context, recoverableKeyStoreDb, userId));
}
/**
int userId,
int credentialType,
String credential,
- PlatformKeyManager.Factory platformKeyManagerFactory,
- VaultKeySupplier vaultKeySupplier) {
+ PlatformKeyManager.Factory platformKeyManagerFactory) {
mSnapshotListenersStorage = recoverySnapshotListenersStorage;
mRecoverableKeyStoreDb = recoverableKeyStoreDb;
mUserId = userId;
mCredentialType = credentialType;
mCredential = credential;
mPlatformKeyManagerFactory = platformKeyManagerFactory;
- mVaultKeySupplier = vaultKeySupplier;
mRecoverySnapshotStorage = snapshotStorage;
}
}
int recoveryAgentUid = mRecoverableKeyStoreDb.getRecoveryAgentUid(mUserId);
-
if (recoveryAgentUid == -1) {
Log.w(TAG, "No recovery agent initialized for user " + mUserId);
return;
return;
}
+ PublicKey publicKey = getVaultPublicKey();
+
+ if (publicKey == null) {
+ Log.w(TAG, "Not initialized for KeySync: no public key set. Cancelling task.");
+ return;
+ }
+
byte[] salt = generateSalt();
byte[] localLskfHash = hashCredentials(salt, mCredential);
byte[] encryptedRecoveryKey;
try {
encryptedRecoveryKey = KeySyncUtils.thmEncryptRecoveryKey(
- mVaultKeySupplier.get(),
+ publicKey,
localLskfHash,
vaultParams,
recoveryKey);
}
private PublicKey getVaultPublicKey() {
- // TODO: fill this in
- throw new UnsupportedOperationException("TODO: get vault public key.");
+ return mRecoverableKeyStoreDb.getRecoveryServicePublicKey(mUserId);
}
/**
}
return keyEntries;
}
-
- /**
- * TODO: until this is in the database, so we can test.
- */
- public interface VaultKeySupplier {
- PublicKey get();
- }
}
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
-import android.security.recoverablekeystore.RecoverableKeyStoreLoader;
import android.text.TextUtils;
import android.util.Log;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
-import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.StringJoiner;
*
* @hide
*/
+ @Nullable
public PublicKey getRecoveryServicePublicKey(int userId, int uid) {
SQLiteDatabase db = mKeyStoreDbHelper.getReadableDatabase();
return null;
}
byte[] keyBytes = cursor.getBlob(idx);
- X509EncodedKeySpec pkSpec = new X509EncodedKeySpec(keyBytes);
try {
- return KeyFactory.getInstance("EC").generatePublic(pkSpec);
- } catch (NoSuchAlgorithmException e) {
- // Should never happen
- throw new RuntimeException(e);
+ return decodeX509Key(keyBytes);
} catch (InvalidKeySpecException e) {
Log.wtf(TAG,
String.format(Locale.US,
return new int[]{};
}
String[] types = csv.split(",");
- int[] result = new int[types.length];
+ int[] result = new int[types.length];
for (int i = 0; i < types.length; i++) {
try {
result[i] = Integer.parseInt(types[i]);
}
/**
+ * Returns the first (and only?) public key for {@code userId}.
+ *
+ * @param userId The uid of the profile whose keys are to be synced.
+ * @return The public key, or null if none exists.
+ */
+ @Nullable
+ public PublicKey getRecoveryServicePublicKey(int userId) {
+ SQLiteDatabase db = mKeyStoreDbHelper.getReadableDatabase();
+
+ String[] projection = { RecoveryServiceMetadataEntry.COLUMN_NAME_PUBLIC_KEY };
+ String selection =
+ RecoveryServiceMetadataEntry.COLUMN_NAME_USER_ID + " = ?";
+ String[] selectionArguments = { Integer.toString(userId) };
+
+ try (
+ Cursor cursor = db.query(
+ RecoveryServiceMetadataEntry.TABLE_NAME,
+ projection,
+ selection,
+ selectionArguments,
+ /*groupBy=*/ null,
+ /*having=*/ null,
+ /*orderBy=*/ null)
+ ) {
+ if (cursor.getCount() < 1) {
+ return null;
+ }
+
+ cursor.moveToFirst();
+ byte[] keyBytes = cursor.getBlob(cursor.getColumnIndexOrThrow(
+ RecoveryServiceMetadataEntry.COLUMN_NAME_PUBLIC_KEY));
+
+ try {
+ return decodeX509Key(keyBytes);
+ } catch (InvalidKeySpecException e) {
+ Log.wtf(TAG, "Could not decode public key for " + userId);
+ return null;
+ }
+ }
+ }
+
+ /**
* Updates the server parameters given by the application initializing the local recovery
* components.
*
mKeyStoreDbHelper.close();
}
- // TODO: Add method for updating the 'last synced' time.
+ @Nullable
+ private static PublicKey decodeX509Key(byte[] keyBytes) throws InvalidKeySpecException {
+ X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(keyBytes);
+ try {
+ return KeyFactory.getInstance("EC").generatePublic(publicKeySpec);
+ } catch (NoSuchAlgorithmException e) {
+ // Should never happen
+ throw new RuntimeException(e);
+ }
+ }
}