1/*
2 * Copyright (C) 2016 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 */
16package com.android.server.pm.backup;
17
18import static com.google.common.truth.Truth.assertThat;
19
20import static org.junit.Assert.assertFalse;
21import static org.junit.Assert.assertTrue;
22import static org.junit.Assert.assertEquals;
23import static org.mockito.Mockito.doReturn;
24import static org.mockito.Mockito.mock;
25import static org.mockito.Mockito.when;
26
27import android.content.pm.ApplicationInfo;
28import android.content.pm.PackageInfo;
29import android.content.pm.PackageManagerInternal;
30import android.content.pm.PackageParser;
31import android.content.pm.PackageParser.Package;
32import android.content.pm.Signature;
33import android.content.pm.SigningInfo;
34import android.test.MoreAsserts;
35import android.platform.test.annotations.Presubmit;
36import android.support.test.filters.SmallTest;
37import android.support.test.runner.AndroidJUnit4;
38
39import com.android.server.backup.BackupUtils;
40
41import org.junit.Before;
42import org.junit.Test;
43import org.junit.runner.RunWith;
44
45import java.util.ArrayList;
46import java.util.Arrays;
47
48@SmallTest
49@Presubmit
50@RunWith(AndroidJUnit4.class)
51public class BackupUtilsTest {
52
53    private static final Signature SIGNATURE_1 = generateSignature((byte) 1);
54    private static final Signature SIGNATURE_2 = generateSignature((byte) 2);
55    private static final Signature SIGNATURE_3 = generateSignature((byte) 3);
56    private static final Signature SIGNATURE_4 = generateSignature((byte) 4);
57    private static final byte[] SIGNATURE_HASH_1 = BackupUtils.hashSignature(SIGNATURE_1);
58    private static final byte[] SIGNATURE_HASH_2 = BackupUtils.hashSignature(SIGNATURE_2);
59    private static final byte[] SIGNATURE_HASH_3 = BackupUtils.hashSignature(SIGNATURE_3);
60    private static final byte[] SIGNATURE_HASH_4 = BackupUtils.hashSignature(SIGNATURE_4);
61
62    private PackageManagerInternal mMockPackageManagerInternal;
63
64    @Before
65    public void setUp() throws Exception {
66        mMockPackageManagerInternal = mock(PackageManagerInternal.class);
67    }
68
69    @Test
70    public void signaturesMatch_targetIsNull_returnsFalse() throws Exception {
71        ArrayList<byte[]> storedSigHashes = new ArrayList<>();
72        storedSigHashes.add(SIGNATURE_HASH_1);
73        boolean result = BackupUtils.signaturesMatch(storedSigHashes, null,
74                mMockPackageManagerInternal);
75
76        assertThat(result).isFalse();
77    }
78
79    @Test
80    public void signaturesMatch_systemApplication_returnsTrue() throws Exception {
81        PackageInfo packageInfo = new PackageInfo();
82        packageInfo.packageName = "test";
83        packageInfo.applicationInfo = new ApplicationInfo();
84        packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
85
86        ArrayList<byte[]> storedSigHashes = new ArrayList<>();
87        storedSigHashes.add(SIGNATURE_HASH_1);
88        boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
89                mMockPackageManagerInternal);
90
91        assertThat(result).isTrue();
92    }
93
94    @Test
95    public void signaturesMatch_disallowsUnsignedApps_storedSignatureNull_returnsFalse()
96            throws Exception {
97        PackageInfo packageInfo = new PackageInfo();
98        packageInfo.packageName = "test";
99        packageInfo.signingInfo = new SigningInfo(
100                new PackageParser.SigningDetails(
101                        new Signature[] {SIGNATURE_1},
102                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
103                        null,
104                        null,
105                        null));
106        packageInfo.applicationInfo = new ApplicationInfo();
107
108        boolean result = BackupUtils.signaturesMatch(null, packageInfo,
109                mMockPackageManagerInternal);
110
111        assertThat(result).isFalse();
112    }
113
114    @Test
115    public void signaturesMatch_disallowsUnsignedApps_storedSignatureEmpty_returnsFalse()
116            throws Exception {
117        PackageInfo packageInfo = new PackageInfo();
118        packageInfo.packageName = "test";
119        packageInfo.signingInfo = new SigningInfo(
120                new PackageParser.SigningDetails(
121                        new Signature[] {SIGNATURE_1},
122                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
123                        null,
124                        null,
125                        null));
126        packageInfo.applicationInfo = new ApplicationInfo();
127
128        ArrayList<byte[]> storedSigHashes = new ArrayList<>();
129        boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
130                mMockPackageManagerInternal);
131
132        assertThat(result).isFalse();
133    }
134
135
136    @Test
137    public void
138    signaturesMatch_disallowsUnsignedApps_targetSignatureEmpty_returnsFalse()
139            throws Exception {
140        PackageInfo packageInfo = new PackageInfo();
141        packageInfo.packageName = "test";
142        packageInfo.signingInfo = null;
143        packageInfo.applicationInfo = new ApplicationInfo();
144
145        ArrayList<byte[]> storedSigHashes = new ArrayList<>();
146        storedSigHashes.add(SIGNATURE_HASH_1);
147        boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
148                mMockPackageManagerInternal);
149
150        assertThat(result).isFalse();
151    }
152
153    @Test
154    public void
155    signaturesMatch_disallowsUnsignedApps_targetSignatureNull_returnsFalse()
156            throws Exception {
157        PackageInfo packageInfo = new PackageInfo();
158        packageInfo.packageName = "test";
159        packageInfo.signingInfo = null;
160        packageInfo.applicationInfo = new ApplicationInfo();
161
162        ArrayList<byte[]> storedSigHashes = new ArrayList<>();
163        storedSigHashes.add(SIGNATURE_HASH_1);
164        boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
165                mMockPackageManagerInternal);
166
167        assertThat(result).isFalse();
168    }
169
170    @Test
171    public void signaturesMatch_disallowsUnsignedApps_bothSignaturesNull_returnsFalse()
172            throws Exception {
173        PackageInfo packageInfo = new PackageInfo();
174        packageInfo.packageName = "test";
175        packageInfo.signingInfo = null;
176        packageInfo.applicationInfo = new ApplicationInfo();
177
178        boolean result = BackupUtils.signaturesMatch(null, packageInfo,
179                mMockPackageManagerInternal);
180
181        assertThat(result).isFalse();
182    }
183
184    @Test
185    public void signaturesMatch_disallowsUnsignedApps_bothSignaturesEmpty_returnsFalse()
186            throws Exception {
187        PackageInfo packageInfo = new PackageInfo();
188        packageInfo.packageName = "test";
189        packageInfo.signingInfo = null;
190        packageInfo.applicationInfo = new ApplicationInfo();
191
192        ArrayList<byte[]> storedSigHashes = new ArrayList<>();
193        boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
194                mMockPackageManagerInternal);
195
196        assertThat(result).isFalse();
197    }
198
199    @Test
200    public void signaturesMatch_equalSignatures_returnsTrue() throws Exception {
201        PackageInfo packageInfo = new PackageInfo();
202        packageInfo.packageName = "test";
203        packageInfo.signingInfo = new SigningInfo(
204                new PackageParser.SigningDetails(
205                        new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
206                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
207                        null,
208                        null,
209                        null));
210        packageInfo.applicationInfo = new ApplicationInfo();
211
212        ArrayList<byte[]> storedSigHashes = new ArrayList<>();
213        storedSigHashes.add(SIGNATURE_HASH_1);
214        storedSigHashes.add(SIGNATURE_HASH_2);
215        storedSigHashes.add(SIGNATURE_HASH_3);
216        boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
217                mMockPackageManagerInternal);
218
219        assertThat(result).isTrue();
220    }
221
222    @Test
223    public void signaturesMatch_extraSignatureInTarget_returnsTrue() throws Exception {
224        PackageInfo packageInfo = new PackageInfo();
225        packageInfo.packageName = "test";
226        packageInfo.signingInfo = new SigningInfo(
227                new PackageParser.SigningDetails(
228                        new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
229                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
230                        null,
231                        null,
232                        null));
233        packageInfo.applicationInfo = new ApplicationInfo();
234
235        ArrayList<byte[]> storedSigHashes = new ArrayList<>();
236        storedSigHashes.add(SIGNATURE_HASH_1);
237        storedSigHashes.add(SIGNATURE_HASH_2);
238        boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
239                mMockPackageManagerInternal);
240
241        assertThat(result).isTrue();
242    }
243
244    @Test
245    public void signaturesMatch_extraSignatureInStored_returnsFalse() throws Exception {
246        PackageInfo packageInfo = new PackageInfo();
247        packageInfo.packageName = "test";
248        packageInfo.signingInfo = new SigningInfo(
249                new PackageParser.SigningDetails(
250                        new Signature[] {SIGNATURE_1, SIGNATURE_2},
251                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
252                        null,
253                        null,
254                        null));
255        packageInfo.applicationInfo = new ApplicationInfo();
256
257        ArrayList<byte[]> storedSigHashes = new ArrayList<>();
258        storedSigHashes.add(SIGNATURE_HASH_1);
259        storedSigHashes.add(SIGNATURE_HASH_2);
260        storedSigHashes.add(SIGNATURE_HASH_3);
261        boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
262                mMockPackageManagerInternal);
263
264        assertThat(result).isFalse();
265    }
266
267    @Test
268    public void signaturesMatch_oneNonMatchingSignature_returnsFalse() throws Exception {
269        PackageInfo packageInfo = new PackageInfo();
270        packageInfo.packageName = "test";
271        packageInfo.signingInfo = new SigningInfo(
272                new PackageParser.SigningDetails(
273                        new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
274                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
275                        null,
276                        null,
277                        null));
278        packageInfo.applicationInfo = new ApplicationInfo();
279
280        ArrayList<byte[]> storedSigHashes = new ArrayList<>();
281        storedSigHashes.add(SIGNATURE_HASH_1);
282        storedSigHashes.add(SIGNATURE_HASH_2);
283        storedSigHashes.add(SIGNATURE_HASH_4);
284        boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
285                mMockPackageManagerInternal);
286
287        assertThat(result).isFalse();
288    }
289
290    @Test
291    public void signaturesMatch_singleStoredSignatureNoRotation_returnsTrue()
292            throws Exception {
293        PackageInfo packageInfo = new PackageInfo();
294        packageInfo.packageName = "test";
295        packageInfo.signingInfo = new SigningInfo(
296                new PackageParser.SigningDetails(
297                        new Signature[] {SIGNATURE_1},
298                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
299                        null,
300                        null,
301                        null));
302        packageInfo.applicationInfo = new ApplicationInfo();
303
304        doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(SIGNATURE_HASH_1,
305                packageInfo.packageName);
306
307        ArrayList<byte[]> storedSigHashes = new ArrayList<>();
308        storedSigHashes.add(SIGNATURE_HASH_1);
309        boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
310                mMockPackageManagerInternal);
311
312        assertThat(result).isTrue();
313    }
314
315    @Test
316    public void signaturesMatch_singleStoredSignatureWithRotationAssumeDataCapability_returnsTrue()
317            throws Exception {
318        PackageInfo packageInfo = new PackageInfo();
319        packageInfo.packageName = "test";
320        packageInfo.signingInfo = new SigningInfo(
321                new PackageParser.SigningDetails(
322                        new Signature[] {SIGNATURE_1, SIGNATURE_2},
323                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
324                        null,
325                        null,
326                        null));
327        packageInfo.applicationInfo = new ApplicationInfo();
328
329        // we know SIGNATURE_1 is in history, and we want to assume it has
330        // SigningDetails.CertCapabilities.INSTALLED_DATA capability
331        doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(SIGNATURE_HASH_1,
332                packageInfo.packageName);
333
334        ArrayList<byte[]> storedSigHashes = new ArrayList<>();
335        storedSigHashes.add(SIGNATURE_HASH_1);
336        boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
337                mMockPackageManagerInternal);
338
339        assertThat(result).isTrue();
340    }
341
342    @Test
343    public void
344            signaturesMatch_singleStoredSignatureWithRotationAssumeNoDataCapability_returnsFalse()
345            throws Exception {
346        PackageInfo packageInfo = new PackageInfo();
347        packageInfo.packageName = "test";
348        packageInfo.signingInfo = new SigningInfo(
349                new PackageParser.SigningDetails(
350                        new Signature[] {SIGNATURE_1, SIGNATURE_2},
351                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
352                        null,
353                        null,
354                        null));
355        packageInfo.applicationInfo = new ApplicationInfo();
356
357        // we know SIGNATURE_1 is in history, but we want to assume it does not have
358        // SigningDetails.CertCapabilities.INSTALLED_DATA capability
359        doReturn(false).when(mMockPackageManagerInternal).isDataRestoreSafe(SIGNATURE_HASH_1,
360                packageInfo.packageName);
361
362        ArrayList<byte[]> storedSigHashes = new ArrayList<>();
363        storedSigHashes.add(SIGNATURE_HASH_1);
364        boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
365                mMockPackageManagerInternal);
366
367        assertThat(result).isFalse();
368    }
369
370    @Test
371    public void testHashSignature() {
372        final byte[] sig1 = "abc".getBytes();
373        final byte[] sig2 = "def".getBytes();
374
375        final byte[] hash1a = BackupUtils.hashSignature(sig1);
376        final byte[] hash1b = BackupUtils.hashSignature(new Signature(sig1));
377
378        final byte[] hash2a = BackupUtils.hashSignature(sig2);
379        final byte[] hash2b = BackupUtils.hashSignature(new Signature(sig2));
380
381        assertEquals(32, hash1a.length);
382        MoreAsserts.assertEquals(hash1a, hash1b);
383
384        assertEquals(32, hash2a.length);
385        MoreAsserts.assertEquals(hash2a, hash2b);
386
387        assertFalse(Arrays.equals(hash1a, hash2a));
388
389        final ArrayList<byte[]> listA = BackupUtils.hashSignatureArray(Arrays.asList(
390                "abc".getBytes(), "def".getBytes()));
391
392        final ArrayList<byte[]> listB = BackupUtils.hashSignatureArray(new Signature[]{
393                new Signature("abc".getBytes()), new Signature("def".getBytes())});
394
395        assertEquals(2, listA.size());
396        assertEquals(2, listB.size());
397
398        MoreAsserts.assertEquals(hash1a, listA.get(0));
399        MoreAsserts.assertEquals(hash1a, listB.get(0));
400
401        MoreAsserts.assertEquals(hash2a, listA.get(1));
402        MoreAsserts.assertEquals(hash2a, listB.get(1));
403    }
404
405    private static Signature generateSignature(byte i) {
406        byte[] signatureBytes = new byte[256];
407        signatureBytes[0] = i;
408        return new Signature(signatureBytes);
409    }
410}
411