1 /*
2  * Copyright 2017 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 
18 package com.android.server.net.watchlist;
19 
20 import android.annotation.Nullable;
21 import android.util.Log;
22 import android.util.proto.ProtoOutputStream;
23 
24 import com.android.internal.annotations.VisibleForTesting;
25 import com.android.internal.util.HexDump;
26 import com.android.service.NetworkWatchlistReportProto;
27 import com.android.service.NetworkWatchlistAppResultProto;
28 
29 import java.io.ByteArrayOutputStream;
30 import java.util.ArrayList;
31 import java.util.Collections;
32 import java.util.List;
33 import java.util.Map;
34 
35 /**
36  * Helper class to encode and generate serialized DP encoded watchlist proto report.
37  */
38 class ReportEncoder {
39 
40     private static final String TAG = "ReportEncoder";
41 
42     // Report version number, as file format / parameters can be changed in later version, we need
43     // to have versioning on watchlist report format
44     private static final int REPORT_VERSION = 1;
45 
46     private static final int WATCHLIST_HASH_SIZE = 32;
47 
48     /**
49      * Apply DP on watchlist results, and generate a serialized watchlist report ready to store
50      * in DropBox.
51      */
52     @Nullable
encodeWatchlistReport(WatchlistConfig config, byte[] userSecret, List<String> appDigestList, WatchlistReportDbHelper.AggregatedResult aggregatedResult)53     static byte[] encodeWatchlistReport(WatchlistConfig config, byte[] userSecret,
54             List<String> appDigestList, WatchlistReportDbHelper.AggregatedResult aggregatedResult) {
55         Map<String, Boolean> resultMap = PrivacyUtils.createDpEncodedReportMap(
56                 config.isConfigSecure(), userSecret, appDigestList, aggregatedResult);
57         return serializeReport(config, resultMap);
58     }
59 
60     /**
61      * Convert DP encoded watchlist report into proto format.
62      *
63      * @param encodedReportMap DP encoded watchlist report.
64      * @return Watchlist report in proto format, which will be shared in Dropbox. Null if
65      * watchlist report cannot be generated.
66      */
67     @Nullable
68     @VisibleForTesting
serializeReport(WatchlistConfig config, Map<String, Boolean> encodedReportMap)69     static byte[] serializeReport(WatchlistConfig config,
70             Map<String, Boolean> encodedReportMap) {
71         // TODO: Handle watchlist config changed case
72         final byte[] watchlistHash = config.getWatchlistConfigHash();
73         if (watchlistHash == null) {
74             Log.e(TAG, "No watchlist hash");
75             return null;
76         }
77         if (watchlistHash.length != WATCHLIST_HASH_SIZE) {
78             Log.e(TAG, "Unexpected hash length");
79             return null;
80         }
81         final ByteArrayOutputStream reportOutputStream = new ByteArrayOutputStream();
82         final ProtoOutputStream proto = new ProtoOutputStream(reportOutputStream);
83 
84         // Set report version to report
85         proto.write(NetworkWatchlistReportProto.REPORT_VERSION, REPORT_VERSION);
86         proto.write(NetworkWatchlistReportProto.WATCHLIST_CONFIG_HASH,
87                 HexDump.toHexString(watchlistHash));
88 
89         // Set app digest, encoded_isPha pair to report
90         for (Map.Entry<String, Boolean> entry : encodedReportMap.entrySet()) {
91             String key = entry.getKey();
92             byte[] digest = HexDump.hexStringToByteArray(key);
93             boolean encodedResult = entry.getValue();
94             long token = proto.start(NetworkWatchlistReportProto.APP_RESULT);
95             proto.write(NetworkWatchlistAppResultProto.APP_DIGEST, key);
96             proto.write(NetworkWatchlistAppResultProto.ENCODED_RESULT, encodedResult);
97             proto.end(token);
98         }
99         proto.flush();
100         return reportOutputStream.toByteArray();
101     }
102 }
103