summaryrefslogtreecommitdiff
path: root/packages/BackupEncryption/src
diff options
context:
space:
mode:
authorAl Sutton <alsutton@google.com>2019-08-23 13:41:16 +0100
committerAl Sutton <alsutton@google.com>2019-09-12 13:41:06 +0100
commitbf257cbe234e8d646db3f3aef79b5e9a3ce7a432 (patch)
treeeb7e111145fab229eb92b30800a9ea3266361c4a /packages/BackupEncryption/src
parentd046fb26f4874bdd4ddf9b1e44b79da03d030424 (diff)
Import TertiaryKeyStore
Bring the TertiaryKeyStore class and its test class into the main repo. Bug: 111386661 Test: make RunBackupEncryptionRoboTests Change-Id: I4718ddde737c19836e415af6ca1fd597e79a0a4a
Diffstat (limited to 'packages/BackupEncryption/src')
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/keys/TertiaryKeyStore.java202
1 files changed, 202 insertions, 0 deletions
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/TertiaryKeyStore.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/TertiaryKeyStore.java
new file mode 100644
index 000000000000..01444bf0cd00
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/TertiaryKeyStore.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.keys;
+
+import static com.android.internal.util.Preconditions.checkArgument;
+
+import android.content.Context;
+import android.util.ArrayMap;
+
+import com.android.server.backup.encryption.protos.nano.WrappedKeyProto;
+import com.android.server.backup.encryption.storage.BackupEncryptionDb;
+import com.android.server.backup.encryption.storage.TertiaryKey;
+import com.android.server.backup.encryption.storage.TertiaryKeysTable;
+
+import com.google.protobuf.nano.CodedOutputByteBufferNano;
+
+import java.io.IOException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Map;
+import java.util.Optional;
+
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+
+/**
+ * Stores backup package keys. Each application package has its own {@link SecretKey}, which is used
+ * to encrypt the backup data. These keys are then wrapped by a master backup key, and stored in
+ * their wrapped form on disk and on the backup server.
+ *
+ * <p>For now this code only implements writing to disk. Once the backup server is ready, it will be
+ * extended to sync the keys there, also.
+ */
+public class TertiaryKeyStore {
+
+ private final RecoverableKeyStoreSecondaryKey mSecondaryKey;
+ private final BackupEncryptionDb mDatabase;
+
+ /**
+ * Creates an instance, using {@code secondaryKey} to wrap tertiary keys, and storing them in
+ * the database.
+ */
+ public static TertiaryKeyStore newInstance(
+ Context context, RecoverableKeyStoreSecondaryKey secondaryKey) {
+ return new TertiaryKeyStore(secondaryKey, BackupEncryptionDb.newInstance(context));
+ }
+
+ private TertiaryKeyStore(
+ RecoverableKeyStoreSecondaryKey secondaryKey, BackupEncryptionDb database) {
+ mSecondaryKey = secondaryKey;
+ mDatabase = database;
+ }
+
+ /**
+ * Saves the given key.
+ *
+ * @param applicationName The package name of the application for which this key will be used to
+ * encrypt data. e.g., "com.example.app".
+ * @param key The key.
+ * @throws InvalidKeyException if the backup key is not capable of wrapping.
+ * @throws IOException if there is an issue writing to the database.
+ */
+ public void save(String applicationName, SecretKey key)
+ throws IOException, InvalidKeyException, IllegalBlockSizeException,
+ NoSuchPaddingException, NoSuchAlgorithmException {
+ checkApplicationName(applicationName);
+
+ byte[] keyBytes = getEncodedKey(KeyWrapUtils.wrap(mSecondaryKey.getSecretKey(), key));
+
+ long pk;
+ try {
+ pk =
+ mDatabase
+ .getTertiaryKeysTable()
+ .addKey(
+ new TertiaryKey(
+ mSecondaryKey.getAlias(), applicationName, keyBytes));
+ } finally {
+ mDatabase.close();
+ }
+
+ if (pk == -1) {
+ throw new IOException("Failed to commit to db");
+ }
+ }
+
+ /**
+ * Tries to load a key for the given application.
+ *
+ * @param applicationName The package name of the application, e.g. "com.example.app".
+ * @return The key if it is exists, {@link Optional#empty()} ()} otherwise.
+ * @throws InvalidKeyException if the backup key is not good for unwrapping.
+ * @throws IOException if there is a problem loading the key from the database.
+ */
+ public Optional<SecretKey> load(String applicationName)
+ throws IOException, InvalidKeyException, InvalidAlgorithmParameterException,
+ NoSuchAlgorithmException, NoSuchPaddingException {
+ checkApplicationName(applicationName);
+
+ Optional<TertiaryKey> keyFromDb;
+ try {
+ keyFromDb =
+ mDatabase
+ .getTertiaryKeysTable()
+ .getKey(mSecondaryKey.getAlias(), applicationName);
+ } finally {
+ mDatabase.close();
+ }
+
+ if (!keyFromDb.isPresent()) {
+ return Optional.empty();
+ }
+
+ WrappedKeyProto.WrappedKey wrappedKey =
+ WrappedKeyProto.WrappedKey.parseFrom(keyFromDb.get().getWrappedKeyBytes());
+ return Optional.of(KeyWrapUtils.unwrap(mSecondaryKey.getSecretKey(), wrappedKey));
+ }
+
+ /**
+ * Loads keys for all applications.
+ *
+ * @return All of the keys in a map keyed by package name.
+ * @throws IOException if there is an issue loading from the database.
+ * @throws InvalidKeyException if the backup key is not an appropriate key for unwrapping.
+ */
+ public Map<String, SecretKey> getAll()
+ throws IOException, InvalidKeyException, InvalidAlgorithmParameterException,
+ NoSuchAlgorithmException, NoSuchPaddingException {
+ Map<String, TertiaryKey> tertiaries;
+ try {
+ tertiaries = mDatabase.getTertiaryKeysTable().getAllKeys(mSecondaryKey.getAlias());
+ } finally {
+ mDatabase.close();
+ }
+
+ Map<String, SecretKey> unwrappedKeys = new ArrayMap<>();
+ for (String applicationName : tertiaries.keySet()) {
+ WrappedKeyProto.WrappedKey wrappedKey =
+ WrappedKeyProto.WrappedKey.parseFrom(
+ tertiaries.get(applicationName).getWrappedKeyBytes());
+ unwrappedKeys.put(
+ applicationName, KeyWrapUtils.unwrap(mSecondaryKey.getSecretKey(), wrappedKey));
+ }
+
+ return unwrappedKeys;
+ }
+
+ /**
+ * Adds all wrapped keys to the database.
+ *
+ * @throws IOException if an error occurred adding a wrapped key.
+ */
+ public void putAll(Map<String, WrappedKeyProto.WrappedKey> wrappedKeysByApplicationName)
+ throws IOException {
+ TertiaryKeysTable tertiaryKeysTable = mDatabase.getTertiaryKeysTable();
+ try {
+
+ for (String applicationName : wrappedKeysByApplicationName.keySet()) {
+ byte[] keyBytes = getEncodedKey(wrappedKeysByApplicationName.get(applicationName));
+ long primaryKey =
+ tertiaryKeysTable.addKey(
+ new TertiaryKey(
+ mSecondaryKey.getAlias(), applicationName, keyBytes));
+
+ if (primaryKey == -1) {
+ throw new IOException("Failed to commit to db");
+ }
+ }
+
+ } finally {
+ mDatabase.close();
+ }
+ }
+
+ private static void checkApplicationName(String applicationName) {
+ checkArgument(!applicationName.isEmpty(), "applicationName must not be empty string.");
+ checkArgument(!applicationName.contains("/"), "applicationName must not contain slash.");
+ }
+
+ private byte[] getEncodedKey(WrappedKeyProto.WrappedKey key) throws IOException {
+ byte[] buffer = new byte[key.getSerializedSize()];
+ CodedOutputByteBufferNano out = CodedOutputByteBufferNano.newInstance(buffer);
+ key.writeTo(out);
+ return buffer;
+ }
+}