1 /*
2  * Copyright (C) 2018 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 package com.android.server.pm;
17 
18 import static org.junit.Assert.assertEquals;
19 import static org.junit.Assert.assertNotNull;
20 import static org.junit.Assert.assertFalse;
21 import static org.junit.Assert.assertTrue;
22 import static org.junit.Assert.fail;
23 
24 import android.content.Context;
25 import android.content.pm.PackageParser;
26 import android.content.pm.Signature;
27 import android.util.Xml;
28 
29 import androidx.test.InstrumentationRegistry;
30 import androidx.test.runner.AndroidJUnit4;
31 
32 import com.android.internal.util.HexDump;
33 
34 import org.junit.Before;
35 import org.junit.Test;
36 import org.junit.runner.RunWith;
37 import org.xmlpull.v1.XmlPullParser;
38 
39 import java.io.File;
40 import java.io.InputStream;
41 import java.nio.charset.StandardCharsets;
42 import java.util.ArrayList;
43 import java.util.HashMap;
44 import java.util.HashSet;
45 import java.util.Map;
46 import java.util.Set;
47 
48 @RunWith(AndroidJUnit4.class)
49 public class PackageSignaturesTest {
50     private static final String TEST_RESOURCES_FOLDER = "PackageSignaturesTest";
51 
52     private Context mContext;
53 
54     private PackageSetting mPackageSetting;
55 
56     // These signatures are the DER encoding of the ec-p256[_X] X509 certificates in the certs/
57     // directory. The apksigner tool was used to sign a test APK with these certificates and the
58     // corresponding ec-p256{_X].pk8 private key file. For the lineage tests the
59     // ec-p256-lineage-X-signers file was provided as the parameter to the --lineage option when
60     // signing the APK. The APK was then installed on a test device, the packages.xml file was
61     // pulled from the device, and the APK's <sig> tag was used as the basis for these tests.
62     // For more details see the README under the xml/ directory.
63     private static final String FIRST_EXPECTED_SIGNATURE =
64             "3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06"
65             + "035504030c0765632d70323536301e170d3136303333313134353830365a170d34333038313731343538"
66             + "30365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce"
67             + "3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194"
68             + "b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04"
69             + "160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b"
70             + "30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349"
71             + "003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8"
72             + "eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd";
73     private static final String SECOND_EXPECTED_SIGNATURE =
74             "3082016d30820113a0030201020209008855bd1dd2b2b225300a06082a8648ce3d04030230123110300e06"
75             + "035504030c0765632d70323536301e170d3138303731333137343135315a170d32383037313031373431"
76             + "35315a30143112301006035504030c0965632d703235365f323059301306072a8648ce3d020106082a86"
77             + "48ce3d030107034200041d4cca0472ad97ee3cecef0da93d62b450c6788333b36e7553cde9f74ab5df00"
78             + "bbba6ba950e68461d70bbc271b62151dad2de2bf6203cd2076801c7a9d4422e1a350304e301d0603551d"
79             + "0e041604147991d92b0208fc448bf506d4efc9fff428cb5e5f301f0603551d23041830168014d4133568"
80             + "b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d040302"
81             + "034800304502202769abb1b49fc2f53479c4ae92a6631dabfd522c9acb0bba2b43ebeb99c63011022100"
82             + "d260fb1d1f176cf9b7fa60098bfd24319f4905a3e5fda100a6fe1a2ab19ff09e";
83     private static final String THIRD_EXPECTED_SIGNATURE =
84             "3082016e30820115a0030201020209008394f5cad16a89a7300a06082a8648ce3d04030230143112301006"
85             + "035504030c0965632d703235365f32301e170d3138303731343030303532365a170d3238303731313030"
86             + "303532365a30143112301006035504030c0965632d703235365f333059301306072a8648ce3d02010608"
87             + "2a8648ce3d03010703420004f31e62430e9db6fc5928d975fc4e47419bacfcb2e07c89299e6cd7e344dd"
88             + "21adfd308d58cb49a1a2a3fecacceea4862069f30be1643bcc255040d8089dfb3743a350304e301d0603"
89             + "551d0e041604146f8d0828b13efaf577fc86b0e99fa3e54bcbcff0301f0603551d230418301680147991"
90             + "d92b0208fc448bf506d4efc9fff428cb5e5f300c0603551d13040530030101ff300a06082a8648ce3d04"
91             + "030203470030440220256bdaa2784c273e4cc291a595a46779dee9de9044dc9f7ab820309567df9fe902"
92             + "201a4ad8c69891b5a8c47434fe9540ed1f4979b5fad3483f3fa04d5677355a579e";
93 
94     // When running tests using the pastSigs tag / lineage the past signers and their capabilities
95     // should be returned in the SigningDetails. The flags attribute of the cert tag under the
96     // pastSigs tag contains these capabilities; for tests that verify the lineage the capabilities
97     // of the signers should be set to the values in this Map.
98     private static final Map<String, Integer> SIGNATURE_TO_CAPABILITY_MAP;
99 
100     static {
101         SIGNATURE_TO_CAPABILITY_MAP = new HashMap<>();
SIGNATURE_TO_CAPABILITY_MAP.put(FIRST_EXPECTED_SIGNATURE, 3)102         SIGNATURE_TO_CAPABILITY_MAP.put(FIRST_EXPECTED_SIGNATURE, 3);
SIGNATURE_TO_CAPABILITY_MAP.put(SECOND_EXPECTED_SIGNATURE, 7)103         SIGNATURE_TO_CAPABILITY_MAP.put(SECOND_EXPECTED_SIGNATURE, 7);
SIGNATURE_TO_CAPABILITY_MAP.put(THIRD_EXPECTED_SIGNATURE, 23)104         SIGNATURE_TO_CAPABILITY_MAP.put(THIRD_EXPECTED_SIGNATURE, 23);
105     }
106 
107     private static final int[] CAPABILITIES =
108             {PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA,
109                     PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID,
110                     PackageParser.SigningDetails.CertCapabilities.PERMISSION,
111                     PackageParser.SigningDetails.CertCapabilities.ROLLBACK};
112 
113     @Before
setUp()114     public void setUp() throws Exception {
115         mContext = InstrumentationRegistry.getContext();
116         mPackageSetting = createPackageSetting();
117     }
118 
119     @Test
testReadXmlWithOneSignerCompletesSuccessfully()120     public void testReadXmlWithOneSignerCompletesSuccessfully() throws Exception {
121         // Verifies the good path of reading a single sigs tag with one signer returns the
122         // expected signature and scheme version.
123         verifyReadXmlReturnsExpectedSignatures("xml/one-signer.xml", 1, FIRST_EXPECTED_SIGNATURE);
124     }
125 
126     @Test
testReadXmlWithTwoV1V2Signers()127     public void testReadXmlWithTwoV1V2Signers() throws Exception {
128         // Verifies the good path of reading a single sigs tag with multiple signers returns the
129         // expected signatures and scheme version.
130         verifyReadXmlReturnsExpectedSignatures("xml/two-signers-v1v2.xml", 2,
131                 FIRST_EXPECTED_SIGNATURE, SECOND_EXPECTED_SIGNATURE);
132     }
133 
134     @Test
testReadXmlFromTwoSigsTagsWithSameSigner()135     public void testReadXmlFromTwoSigsTagsWithSameSigner() throws Exception {
136         // Verifies the good path of reading two separate packages tags from the same signer. The
137         // first call to readXml should return the list with the expected signature, then the second
138         // call should reference this signature and complete successfully with no new entries in the
139         // List.
140         XmlPullParser parser = getXMLFromResources("xml/one-signer.xml");
141         ArrayList<Signature> signatures = new ArrayList<>();
142         mPackageSetting.signatures.readXml(parser, signatures);
143         Set<String> expectedSignatures = createSetOfSignatures(FIRST_EXPECTED_SIGNATURE);
144         verifySignaturesContainExpectedValues(signatures, expectedSignatures);
145         parser = getXMLFromResources("xml/one-signer-previous-cert.xml");
146         mPackageSetting.signatures.readXml(parser, signatures);
147         expectedSignatures = createSetOfSignatures(FIRST_EXPECTED_SIGNATURE);
148         verifySignaturesContainExpectedValues(signatures, expectedSignatures);
149     }
150 
151     @Test
testReadXmlWithSigningLineage()152     public void testReadXmlWithSigningLineage() throws Exception {
153         // Verifies the good path of reading a single sigs tag including pastSigs with the
154         // signing lineage returns the expected signatures and lineage for two and three signers
155         // in the lineage.
156         verifyReadXmlReturnsExpectedSignaturesAndLineage("xml/two-signers-in-lineage.xml", 3,
157                 FIRST_EXPECTED_SIGNATURE, SECOND_EXPECTED_SIGNATURE);
158         verifyReadXmlReturnsExpectedSignaturesAndLineage("xml/three-signers-in-lineage.xml", 3,
159                 FIRST_EXPECTED_SIGNATURE, SECOND_EXPECTED_SIGNATURE, THIRD_EXPECTED_SIGNATURE);
160     }
161 
162     @Test
testReadXmlWithInvalidPublicKeyInCertKey()163     public void testReadXmlWithInvalidPublicKeyInCertKey() throws Exception {
164         // If the cert tag key attribute does not contain a valid public key then a
165         // CertificateException should be thrown when attempting to build the SigningDetails; in
166         // this case the signing details should be set to UNKNOWN.
167         XmlPullParser parser = getXMLFromResources(
168                 "xml/one-signer-invalid-public-key-cert-key.xml");
169         ArrayList<Signature> signatures = new ArrayList<>();
170         mPackageSetting.signatures.readXml(parser, signatures);
171         assertEquals(
172                 "The signing details was not UNKNOWN after parsing an invalid public key cert key"
173                         + " attribute",
174                 PackageParser.SigningDetails.UNKNOWN, mPackageSetting.signatures.mSigningDetails);
175     }
176 
177     @Test
testReadXmlWithMissingSigsCount()178     public void testReadXmlWithMissingSigsCount() throws Exception {
179         // Verifies if the sigs count attribute is missing then the signature cannot be read but the
180         // method does not throw an exception.
181         verifyReadXmlReturnsExpectedSignatures("xml/one-signer-missing-sigs-count.xml",
182                 PackageParser.SigningDetails.SignatureSchemeVersion.UNKNOWN);
183     }
184 
185     @Test
testReadXmlWithMissingSchemeVersion()186     public void testReadXmlWithMissingSchemeVersion() throws Exception {
187         // Verifies if the schemeVersion is an invalid value the signature can still be obtained.
188         verifyReadXmlReturnsExpectedSignatures("xml/one-signer-missing-scheme-version.xml",
189                 PackageParser.SigningDetails.SignatureSchemeVersion.UNKNOWN,
190                 FIRST_EXPECTED_SIGNATURE);
191     }
192 
193     @Test
testReadXmlWithSigningLineageWithMissingSchemeVersion()194     public void testReadXmlWithSigningLineageWithMissingSchemeVersion() throws Exception {
195         // Verifies if the scheme version cannot be read the signers in the lineage can still be
196         // obtained.
197         verifyReadXmlReturnsExpectedSignaturesAndLineage(
198                 "xml/three-signers-in-lineage-missing-scheme-version.xml",
199                 PackageParser.SigningDetails.SignatureSchemeVersion.UNKNOWN,
200                 FIRST_EXPECTED_SIGNATURE, SECOND_EXPECTED_SIGNATURE, THIRD_EXPECTED_SIGNATURE);
201     }
202 
203     @Test
testReadXmlWithInvalidCertIndex()204     public void testReadXmlWithInvalidCertIndex() throws Exception {
205         // If the cert index attribute is invalid the signature will not be read but the call
206         // should exit gracefully.
207         verifyReadXmlReturnsExpectedSignatures("xml/one-signer-invalid-cert-index.xml", 3);
208     }
209 
210     @Test
testReadXmlWithMissingCertIndex()211     public void testReadXmlWithMissingCertIndex() throws Exception {
212         // If the cert index attribute is missing the signature will not be read but the call should
213         // exit gracefully.
214         verifyReadXmlReturnsExpectedSignatures("xml/one-signer-missing-cert-index.xml", 3);
215     }
216 
217     @Test
testReadXmlWithInvalidCertKey()218     public void testReadXmlWithInvalidCertKey() throws Exception {
219         // If the cert key value is invalid the signature cannot be read but the call should exit
220         // gracefully.
221         verifyReadXmlReturnsExpectedSignatures("xml/one-signer-invalid-cert-key.xml", 3);
222     }
223 
224     @Test
testReadXmlWithMissingCertKey()225     public void testReadXmlWithMissingCertKey() throws Exception {
226         // If the cert key is missing the signature cannot be read but the call should exit
227         // gracefully.
228         verifyReadXmlReturnsExpectedSignatures("xml/one-signer-missing-cert-key.xml", 3);
229     }
230 
231     @Test
testReadXmlWithMissingCertTag()232     public void testReadXmlWithMissingCertTag() throws Exception {
233         // If the cert tag is missing there is no signature to read but the call should exit
234         // gracefully.
235         verifyReadXmlReturnsExpectedSignatures("xml/one-signer-missing-cert-tag.xml", 3);
236     }
237 
238     @Test
testReadXmlWithTooFewCertTags()239     public void testReadXmlWithTooFewCertTags() throws Exception {
240         // If the number of cert tags is less than that specified in the count attribute then the
241         // signatures that could be read are copied to a smaller array to be used when building
242         // the SigningDetails object. This test verifies if there are too few cert tags the
243         // available signatures can still be obtained.
244         verifyReadXmlReturnsExpectedSignatures("xml/two-signers-v1v2-missing-cert-tag.xml", 1,
245                 FIRST_EXPECTED_SIGNATURE);
246     }
247 
248     @Test
testReadXmlWithExtraCertTag()249     public void testReadXmlWithExtraCertTag() throws Exception {
250         // Verifies if there are more cert tags than specified by the count attribute the extra cert
251         // tag is ignored and the expected signature from the first cert tag is returned.
252         verifyReadXmlReturnsExpectedSignatures("xml/one-signer-extra-cert-tag.xml", 3,
253                 FIRST_EXPECTED_SIGNATURE);
254     }
255 
256     @Test
testReadXmlWithInvalidTag()257     public void testReadXmlWithInvalidTag() throws Exception {
258         // Verifies an invalid tag under sigs is ignored and the expected signature is returned.
259         verifyReadXmlReturnsExpectedSignatures("xml/one-signer-invalid-tag.xml", 3,
260                 FIRST_EXPECTED_SIGNATURE);
261     }
262 
263     @Test
testReadXmlWithInvalidPastSigsCount()264     public void testReadXmlWithInvalidPastSigsCount() throws Exception {
265         // Verifies if the pastSigs tag contains an invalid count attribute the current signature
266         // is still returned; in this case the third expected signature is the most recent signer.
267         verifyReadXmlReturnsExpectedSignatures(
268                 "xml/three-signers-in-lineage-invalid-pastSigs-count.xml", 3,
269                 THIRD_EXPECTED_SIGNATURE);
270     }
271 
272     @Test
testReadXmlWithMissingPastSigsCount()273     public void testReadXmlWithMissingPastSigsCount() throws Exception {
274         // Verifies if the pastSigs tag is missing the count attribute the current signature is
275         // still returned; in this case the third expected signature is the most recent signer.
276         verifyReadXmlReturnsExpectedSignaturesAndLineage(
277                 "xml/three-signers-in-lineage-missing-pastSigs-count.xml", 3,
278                 THIRD_EXPECTED_SIGNATURE);
279     }
280 
281     @Test
testReadXmlWithInvalidCertFlags()282     public void testReadXmlWithInvalidCertFlags() throws Exception {
283         // Verifies if the cert tag contains an invalid flags attribute the expected signatures
284         // are still returned, although since the flags could not be read these signatures will not
285         // include the capabilities of the previous signers in the lineage.
286         verifyReadXmlReturnsExpectedSignatures("xml/two-signers-in-lineage-invalid-certs-flags.xml",
287                 3, FIRST_EXPECTED_SIGNATURE, SECOND_EXPECTED_SIGNATURE);
288     }
289 
290     @Test
testReadXmlWithMissingCertFlags()291     public void testReadXmlWithMissingCertFlags() throws Exception {
292         // Verifies if the cert tag does not contain a flags attribute the expected signatures are
293         // still returned, although since there are no flags to read these signatures will not
294         // include the capabilities of the previous signers in the lineage.
295         verifyReadXmlReturnsExpectedSignatures("xml/two-signers-in-lineage-missing-certs-flags.xml",
296                 3, FIRST_EXPECTED_SIGNATURE, SECOND_EXPECTED_SIGNATURE);
297     }
298 
299     @Test
testReadXmlWithMultiplePastSigsTags()300     public void testReadXmlWithMultiplePastSigsTags() throws Exception {
301         // Verifies if multiple pastSigs tags are found under the sigs tag the additional pastSigs
302         // tag is ignored and the expected signatures are returned along with the previous signer in
303         // the lineage.
304         verifyReadXmlReturnsExpectedSignaturesAndLineage(
305                 "xml/two-signers-in-lineage-multiple-pastSigs-tags.xml", 3,
306                 FIRST_EXPECTED_SIGNATURE, SECOND_EXPECTED_SIGNATURE);
307     }
308 
309     @Test
testReadXmlWithInvalidPastSigsCertIndex()310     public void testReadXmlWithInvalidPastSigsCertIndex() throws Exception {
311         // If the pastSigs cert tag contains an invalid index attribute that signature cannot be
312         // read but the current signature should still be returned.
313         verifyReadXmlReturnsExpectedSignaturesAndLineage(
314                 "xml/two-signers-in-lineage-invalid-pastSigs-cert-index.xml", 3,
315                 SECOND_EXPECTED_SIGNATURE);
316     }
317 
318     @Test
testReadXmlWithMissingPastSigsCertIndex()319     public void testReadXmlWithMissingPastSigsCertIndex() throws Exception {
320         // If the pastSigs cert tag does not contain an index attribute that signature cannot be
321         // read but the current signature should still be returned.
322         verifyReadXmlReturnsExpectedSignaturesAndLineage(
323                 "xml/two-signers-in-lineage-missing-pastSigs-cert-index.xml", 3,
324                 SECOND_EXPECTED_SIGNATURE);
325     }
326 
327     @Test
testReadXmlWithUndefinedPastSigsIndex()328     public void testReadXmlWithUndefinedPastSigsIndex() throws Exception {
329         // If a cert tag does not contain a key attribute it is assumed that the index attribute
330         // refers to a previously seen signature. If a signature does not yet exist at this index
331         // then the current signature cannot be read but any other signatures should still be
332         // returned.
333         verifyReadXmlReturnsExpectedSignatures(
334                 "xml/two-signers-in-lineage-undefined-pastSigs-index.xml", 3,
335                 FIRST_EXPECTED_SIGNATURE, null);
336     }
337 
338     @Test
testReadXmlWithTooFewPastSigsCertTags()339     public void testReadXmlWithTooFewPastSigsCertTags() throws Exception {
340         // If the number of cert tags is less than that specified in the count attribute of the
341         // pastSigs tag then the signatures that could be read are copied to a smaller array to be
342         // used when building the SigningDetails object. This test verifies if there are too few
343         // cert tags the available signatures and lineage can still be obtained.
344         verifyReadXmlReturnsExpectedSignaturesAndLineage(
345                 "xml/three-signers-in-lineage-missing-pastSigs-cert-tag.xml", 3,
346                 FIRST_EXPECTED_SIGNATURE, THIRD_EXPECTED_SIGNATURE);
347     }
348 
349     @Test
testReadXmlWithPastSignerWithNoCapabilities()350     public void testReadXmlWithPastSignerWithNoCapabilities() throws Exception {
351         // When rotating the signing key a developer is able to specify the capabilities granted to
352         // the apps signed with the previous key. This test verifies a previous signing certificate
353         // with the flags set to 0 does not have any capabilities.
354         XmlPullParser parser = getXMLFromResources("xml/two-signers-in-lineage-no-caps.xml");
355         ArrayList<Signature> signatures = new ArrayList<>();
356         mPackageSetting.signatures.readXml(parser, signatures);
357         // obtain the Signature in the list matching the previous signing certificate
358         Signature previousSignature = null;
359         for (Signature signature : signatures) {
360             String signatureValue = HexDump.toHexString(signature.toByteArray(), false);
361             if (signatureValue.equals(FIRST_EXPECTED_SIGNATURE)) {
362                 previousSignature = signature;
363                 break;
364             }
365         }
366         assertNotNull("Unable to find the expected previous signer", previousSignature);
367         for (int capability : CAPABILITIES) {
368             assertFalse("The previous signer should not have the " + capability + " capability",
369                     mPackageSetting.signatures.mSigningDetails.hasCertificate(previousSignature,
370                             capability));
371         }
372     }
373 
374     /**
375      * Verifies reading the sigs tag of the provided XML file returns the specified signature scheme
376      * version and the provided signatures.
377      */
verifyReadXmlReturnsExpectedSignatures(String xmlFile, int expectedSchemeVersion, String... expectedSignatureValues)378     private void verifyReadXmlReturnsExpectedSignatures(String xmlFile, int expectedSchemeVersion,
379             String... expectedSignatureValues) throws Exception {
380         XmlPullParser parser = getXMLFromResources(xmlFile);
381         ArrayList<Signature> signatures = new ArrayList<>();
382         mPackageSetting.signatures.readXml(parser, signatures);
383         Set<String> expectedSignatures = createSetOfSignatures(expectedSignatureValues);
384         verifySignaturesContainExpectedValues(signatures, expectedSignatures);
385         assertEquals("The returned signature scheme is not the expected value",
386                 expectedSchemeVersion,
387                 mPackageSetting.signatures.mSigningDetails.signatureSchemeVersion);
388     }
389 
390     /**
391      * Verifies reading the sigs tag of the provided XML file returns the specified signature scheme
392      * version, the provided signatures, and that the previous signers have the expected
393      * capabilities.
394      */
verifyReadXmlReturnsExpectedSignaturesAndLineage(String xmlFile, int schemeVersion, String... expectedSignatureValues)395     private void verifyReadXmlReturnsExpectedSignaturesAndLineage(String xmlFile,
396             int schemeVersion, String... expectedSignatureValues) throws Exception {
397         XmlPullParser parser = getXMLFromResources(xmlFile);
398         ArrayList<Signature> signatures = new ArrayList<>();
399         mPackageSetting.signatures.readXml(parser, signatures);
400         Set<String> expectedSignatures = createSetOfSignatures(expectedSignatureValues);
401         verifySignaturesContainExpectedValues(signatures, expectedSignatures);
402         assertEquals("The returned signature scheme is not the expected value", schemeVersion,
403                 mPackageSetting.signatures.mSigningDetails.signatureSchemeVersion);
404         for (Signature signature : signatures) {
405             String signatureValue = HexDump.toHexString(signature.toByteArray(), false);
406             int expectedCapabilities = SIGNATURE_TO_CAPABILITY_MAP.get(signatureValue);
407             assertTrue("The signature " + signatureValue
408                             + " was not found with the expected capabilities of " +
409                             expectedCapabilities
410                             + " in the signing details",
411                     mPackageSetting.signatures.mSigningDetails.hasCertificate(signature,
412                             expectedCapabilities));
413         }
414     }
415 
416     /**
417      * Verifies the provided {@code List} contains Signatures that match the provided hex encoded
418      * signature values.
419      *
420      * The provided {@code Set} will be modified by this method as elements will be removed to
421      * ensure duplicate expected Signatures are not in the {@code List}.
422      */
verifySignaturesContainExpectedValues(ArrayList<Signature> signatures, Set<String> expectedSignatures)423     private static void verifySignaturesContainExpectedValues(ArrayList<Signature> signatures,
424             Set<String> expectedSignatures) {
425         assertEquals("The number of signatures does not equal the expected number of signatures",
426                 expectedSignatures.size(), signatures.size());
427         for (Signature signature : signatures) {
428             String signatureString = null;
429             if (signature != null) {
430                 signatureString = HexDump.toHexString(signature.toByteArray(), false);
431             }
432             // If the signature is in the expected set then remove it so that duplicate matching
433             // signatures are reported.
434             if (expectedSignatures.contains(signatureString)) {
435                 expectedSignatures.remove(signatureString);
436             } else {
437                 fail("The following unexpected signature was returned: " + signatureString);
438             }
439         }
440     }
441 
createSetOfSignatures(String... signatures)442     private static Set<String> createSetOfSignatures(String... signatures) {
443         Set<String> result = new HashSet<String>();
444         for (String signature : signatures) {
445             result.add(signature);
446         }
447         return result;
448     }
449 
getXMLFromResources(String xmlFile)450     private XmlPullParser getXMLFromResources(String xmlFile) throws Exception {
451         InputStream xmlStream = mContext.getResources().getAssets().open(
452                 TEST_RESOURCES_FOLDER + "/" + xmlFile);
453         XmlPullParser result = Xml.newPullParser();
454         result.setInput(xmlStream, StandardCharsets.UTF_8.name());
455         int type;
456         // advance the parser to the first tag
457         while ((type = result.next()) != XmlPullParser.START_TAG
458                 && type != XmlPullParser.END_DOCUMENT) {
459             ;
460         }
461         return result;
462     }
463 
createPackageSetting()464     private static PackageSetting createPackageSetting() {
465         // Generic PackageSetting object with values from a test app installed on a device to be
466         // used to test the methods under the PackageSignatures signatures data member.
467         File appPath = new File("/data/app/app");
468         PackageSetting result = new PackageSetting("test.app", null, appPath, appPath,
469                 "/data/app/app", null, null, null,
470                 1, 940097092, 0, null,
471                 null, 0 /*userId*/, null, null);
472         return result;
473     }
474 }
475