• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.backup.encryption.kv;
18 
19 import static com.android.internal.util.Preconditions.checkState;
20 
21 import com.android.internal.annotations.VisibleForTesting;
22 import com.android.server.backup.encryption.chunk.ChunkHash;
23 import com.android.server.backup.encryption.chunking.ChunkHasher;
24 import com.android.server.backup.encryption.protos.nano.KeyValuePairProto;
25 import com.android.server.backup.encryption.tasks.DecryptedChunkOutput;
26 
27 import java.io.IOException;
28 import java.security.InvalidKeyException;
29 import java.security.MessageDigest;
30 import java.security.NoSuchAlgorithmException;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.Collections;
34 import java.util.Comparator;
35 import java.util.List;
36 
37 /**
38  * Builds a key value backup set from plaintext chunks. Computes a digest over the sorted SHA-256
39  * hashes of the chunks.
40  */
41 public class DecryptedChunkKvOutput implements DecryptedChunkOutput {
42     @VisibleForTesting static final String DIGEST_ALGORITHM = "SHA-256";
43 
44     private final ChunkHasher mChunkHasher;
45     private final List<KeyValuePairProto.KeyValuePair> mUnsortedPairs = new ArrayList<>();
46     private final List<ChunkHash> mUnsortedHashes = new ArrayList<>();
47     private boolean mClosed;
48 
49     /** Constructs a new instance which computers the digest using the given hasher. */
DecryptedChunkKvOutput(ChunkHasher chunkHasher)50     public DecryptedChunkKvOutput(ChunkHasher chunkHasher) {
51         mChunkHasher = chunkHasher;
52     }
53 
54     @Override
open()55     public DecryptedChunkOutput open() {
56         // As we don't have any resources there is nothing to open.
57         return this;
58     }
59 
60     @Override
processChunk(byte[] plaintextBuffer, int length)61     public void processChunk(byte[] plaintextBuffer, int length)
62             throws IOException, InvalidKeyException {
63         checkState(!mClosed, "Cannot process chunk after close()");
64         KeyValuePairProto.KeyValuePair kvPair = new KeyValuePairProto.KeyValuePair();
65         KeyValuePairProto.KeyValuePair.mergeFrom(kvPair, plaintextBuffer, 0, length);
66         mUnsortedPairs.add(kvPair);
67         // TODO(b/71492289): Update ChunkHasher to accept offset and length so we don't have to copy
68         // the buffer into a smaller array.
69         mUnsortedHashes.add(mChunkHasher.computeHash(Arrays.copyOf(plaintextBuffer, length)));
70     }
71 
72     @Override
close()73     public void close() {
74         // As we don't have any resources there is nothing to close.
75         mClosed = true;
76     }
77 
78     @Override
getDigest()79     public byte[] getDigest() throws NoSuchAlgorithmException {
80         checkState(mClosed, "Must close() before getDigest()");
81         MessageDigest digest = getMessageDigest();
82         Collections.sort(mUnsortedHashes);
83         for (ChunkHash hash : mUnsortedHashes) {
84             digest.update(hash.getHash());
85         }
86         return digest.digest();
87     }
88 
getMessageDigest()89     private static MessageDigest getMessageDigest() throws NoSuchAlgorithmException {
90         return MessageDigest.getInstance(DIGEST_ALGORITHM);
91     }
92 
93     /**
94      * Returns the key value pairs from the backup, sorted lexicographically by key.
95      *
96      * <p>You must call {@link #close} first.
97      */
getPairs()98     public List<KeyValuePairProto.KeyValuePair> getPairs() {
99         checkState(mClosed, "Must close() before getPairs()");
100         Collections.sort(
101                 mUnsortedPairs,
102                 new Comparator<KeyValuePairProto.KeyValuePair>() {
103                     @Override
104                     public int compare(
105                             KeyValuePairProto.KeyValuePair o1, KeyValuePairProto.KeyValuePair o2) {
106                         return o1.key.compareTo(o2.key);
107                     }
108                 });
109         return mUnsortedPairs;
110     }
111 }
112