summaryrefslogtreecommitdiff
path: root/packages/BackupEncryption/src
diff options
context:
space:
mode:
authorAl Sutton <alsutton@google.com>2019-09-18 14:52:48 +0100
committerAl Sutton <alsutton@google.com>2019-09-23 10:26:27 +0100
commit668c05eb92d0d624a2c7eae9fc35c792a921a721 (patch)
tree8fe6d573c84706cc8ca5443a96ed5a22f6ea1889 /packages/BackupEncryption/src
parente18cb098628a66c3c199f29af92f565f72534970 (diff)
Import the code related to Key/Value backup encryption
Bug: 111386661 Test: make RunBackupEncryptionRoboTests Change-Id: I6adaedf05034b6706a31a071f9ae0f04abc7b300
Diffstat (limited to 'packages/BackupEncryption/src')
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/kv/DecryptedChunkKvOutput.java111
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/kv/KeyValueListingBuilder.java77
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/DecryptedChunkOutput.java8
3 files changed, 193 insertions, 3 deletions
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/kv/DecryptedChunkKvOutput.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/kv/DecryptedChunkKvOutput.java
new file mode 100644
index 000000000000..56e1c053d8e3
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/kv/DecryptedChunkKvOutput.java
@@ -0,0 +1,111 @@
+/*
+ * 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.kv;
+
+import static com.android.internal.util.Preconditions.checkState;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.backup.encryption.chunk.ChunkHash;
+import com.android.server.backup.encryption.chunking.ChunkHasher;
+import com.android.server.backup.encryption.protos.nano.KeyValuePairProto;
+import com.android.server.backup.encryption.tasks.DecryptedChunkOutput;
+
+import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Builds a key value backup set from plaintext chunks. Computes a digest over the sorted SHA-256
+ * hashes of the chunks.
+ */
+public class DecryptedChunkKvOutput implements DecryptedChunkOutput {
+ @VisibleForTesting static final String DIGEST_ALGORITHM = "SHA-256";
+
+ private final ChunkHasher mChunkHasher;
+ private final List<KeyValuePairProto.KeyValuePair> mUnsortedPairs = new ArrayList<>();
+ private final List<ChunkHash> mUnsortedHashes = new ArrayList<>();
+ private boolean mClosed;
+
+ /** Constructs a new instance which computers the digest using the given hasher. */
+ public DecryptedChunkKvOutput(ChunkHasher chunkHasher) {
+ mChunkHasher = chunkHasher;
+ }
+
+ @Override
+ public DecryptedChunkOutput open() {
+ // As we don't have any resources there is nothing to open.
+ return this;
+ }
+
+ @Override
+ public void processChunk(byte[] plaintextBuffer, int length)
+ throws IOException, InvalidKeyException {
+ checkState(!mClosed, "Cannot process chunk after close()");
+ KeyValuePairProto.KeyValuePair kvPair = new KeyValuePairProto.KeyValuePair();
+ KeyValuePairProto.KeyValuePair.mergeFrom(kvPair, plaintextBuffer, 0, length);
+ mUnsortedPairs.add(kvPair);
+ // TODO(b/71492289): Update ChunkHasher to accept offset and length so we don't have to copy
+ // the buffer into a smaller array.
+ mUnsortedHashes.add(mChunkHasher.computeHash(Arrays.copyOf(plaintextBuffer, length)));
+ }
+
+ @Override
+ public void close() {
+ // As we don't have any resources there is nothing to close.
+ mClosed = true;
+ }
+
+ @Override
+ public byte[] getDigest() throws NoSuchAlgorithmException {
+ checkState(mClosed, "Must close() before getDigest()");
+ MessageDigest digest = getMessageDigest();
+ Collections.sort(mUnsortedHashes);
+ for (ChunkHash hash : mUnsortedHashes) {
+ digest.update(hash.getHash());
+ }
+ return digest.digest();
+ }
+
+ private static MessageDigest getMessageDigest() throws NoSuchAlgorithmException {
+ return MessageDigest.getInstance(DIGEST_ALGORITHM);
+ }
+
+ /**
+ * Returns the key value pairs from the backup, sorted lexicographically by key.
+ *
+ * <p>You must call {@link #close} first.
+ */
+ public List<KeyValuePairProto.KeyValuePair> getPairs() {
+ checkState(mClosed, "Must close() before getPairs()");
+ Collections.sort(
+ mUnsortedPairs,
+ new Comparator<KeyValuePairProto.KeyValuePair>() {
+ @Override
+ public int compare(
+ KeyValuePairProto.KeyValuePair o1, KeyValuePairProto.KeyValuePair o2) {
+ return o1.key.compareTo(o2.key);
+ }
+ });
+ return mUnsortedPairs;
+ }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/kv/KeyValueListingBuilder.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/kv/KeyValueListingBuilder.java
new file mode 100644
index 000000000000..b3518e144ce3
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/kv/KeyValueListingBuilder.java
@@ -0,0 +1,77 @@
+/*
+ * 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.kv;
+
+import static com.android.internal.util.Preconditions.checkArgument;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import com.android.server.backup.encryption.chunk.ChunkHash;
+import com.android.server.backup.encryption.protos.nano.KeyValueListingProto;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * Builds a {@link KeyValueListingProto.KeyValueListing}, which is a nano proto and so has no
+ * builder.
+ */
+public class KeyValueListingBuilder {
+ private final List<KeyValueListingProto.KeyValueEntry> mEntries = new ArrayList<>();
+
+ /** Adds a new pair entry to the listing. */
+ public KeyValueListingBuilder addPair(String key, ChunkHash hash) {
+ checkArgument(key.length() != 0, "Key must have non-zero length");
+ checkNotNull(hash, "Hash must not be null");
+
+ KeyValueListingProto.KeyValueEntry entry = new KeyValueListingProto.KeyValueEntry();
+ entry.key = key;
+ entry.hash = hash.getHash();
+ mEntries.add(entry);
+
+ return this;
+ }
+
+ /** Adds all pairs contained in a map, where the map is from key to hash. */
+ public KeyValueListingBuilder addAll(Map<String, ChunkHash> map) {
+ for (Entry<String, ChunkHash> entry : map.entrySet()) {
+ addPair(entry.getKey(), entry.getValue());
+ }
+
+ return this;
+ }
+
+ /** Returns a new listing containing all the pairs added so far. */
+ public KeyValueListingProto.KeyValueListing build() {
+ if (mEntries.size() == 0) {
+ return emptyListing();
+ }
+
+ KeyValueListingProto.KeyValueListing listing = new KeyValueListingProto.KeyValueListing();
+ listing.entries = new KeyValueListingProto.KeyValueEntry[mEntries.size()];
+ mEntries.toArray(listing.entries);
+ return listing;
+ }
+
+ /** Returns a new listing which does not contain any pairs. */
+ public static KeyValueListingProto.KeyValueListing emptyListing() {
+ KeyValueListingProto.KeyValueListing listing = new KeyValueListingProto.KeyValueListing();
+ listing.entries = KeyValueListingProto.KeyValueEntry.emptyArray();
+ return listing;
+ }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/DecryptedChunkOutput.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/DecryptedChunkOutput.java
index e3df3c1eb96f..f67f1007f632 100644
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/DecryptedChunkOutput.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/DecryptedChunkOutput.java
@@ -19,6 +19,7 @@ package com.android.server.backup.encryption.tasks;
import java.io.Closeable;
import java.io.IOException;
import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
/**
* Accepts the plaintext bytes of decrypted chunks and writes them to some output. Also keeps track
@@ -30,7 +31,7 @@ public interface DecryptedChunkOutput extends Closeable {
*
* @return {@code this}, to allow use with try-with-resources
*/
- DecryptedChunkOutput open() throws IOException;
+ DecryptedChunkOutput open() throws IOException, NoSuchAlgorithmException;
/**
* Writes the plaintext bytes of chunk to whatever output the implementation chooses. Also
@@ -43,12 +44,13 @@ public interface DecryptedChunkOutput extends Closeable {
* at index 0.
* @param length The length in bytes of the plaintext contained in {@code plaintextBuffer}.
*/
- void processChunk(byte[] plaintextBuffer, int length) throws IOException, InvalidKeyException;
+ void processChunk(byte[] plaintextBuffer, int length)
+ throws IOException, InvalidKeyException, NoSuchAlgorithmException;
/**
* Returns the message digest of all the chunks processed by {@link #processChunk}.
*
* <p>You must call {@link Closeable#close()} before calling this method.
*/
- byte[] getDigest();
+ byte[] getDigest() throws NoSuchAlgorithmException;
}