From 7c001ed2f3366ee1876c8aa9e85224908256b584 Mon Sep 17 00:00:00 2001 From: Michael Groover Date: Wed, 10 Feb 2021 21:38:21 -0800 Subject: [PATCH] Include knownCerts in Parcels for PermissionInfo and ParsedPermission Android 12 adds support for a new knownSigner permission protection flag that allows one or more trusted signing certificates to be specified with the permission declaration; a requesting app signed by any of these certificates will be granted the permission. This commits adds the new knownCerts Set to the Parcels for PermissionInfo and ParsedPermission instances. Bug: 172970984 Test: atest PkgInstallSignatureVerificationTest Test: atest PermissionInfoTest Change-Id: I07e415c0d3df6fe352ec5b04a29d986a2647fa19 --- .../android/content/pm/PermissionInfo.java | 7 +++ .../parsing/component/ParsedPermission.java | 21 +++++++ .../component/ParsedPermissionUtils.java | 14 +---- .../content/pm/PermissionInfoTest.java | 61 +++++++++++++++++++ 4 files changed, 92 insertions(+), 11 deletions(-) create mode 100644 core/tests/coretests/src/android/content/pm/PermissionInfoTest.java diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java index a2e533af64a0c..0e70a3e4e600b 100644 --- a/core/java/android/content/pm/PermissionInfo.java +++ b/core/java/android/content/pm/PermissionInfo.java @@ -28,6 +28,9 @@ import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; +import com.android.internal.util.Parcelling; +import com.android.internal.util.Parcelling.BuiltIn.ForStringSet; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Set; @@ -477,6 +480,8 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { */ public @Nullable CharSequence nonLocalizedDescription; + private static ForStringSet sForStringSet = Parcelling.Cache.getOrCreate(ForStringSet.class); + /** * A {@link Set} of trusted signing certificate digests. If this permission has the {@link * #PROTECTION_FLAG_KNOWN_SIGNER} flag set the permission will be granted to a requesting app @@ -688,6 +693,7 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { dest.writeInt(descriptionRes); dest.writeInt(requestRes); TextUtils.writeToParcel(nonLocalizedDescription, dest, parcelableFlags); + sForStringSet.parcel(knownCerts, dest, parcelableFlags); } /** @hide */ @@ -753,5 +759,6 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { descriptionRes = source.readInt(); requestRes = source.readInt(); nonLocalizedDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source); + knownCerts = sForStringSet.unparcel(source); } } diff --git a/core/java/android/content/pm/parsing/component/ParsedPermission.java b/core/java/android/content/pm/parsing/component/ParsedPermission.java index 35bb33c84d567..37e0e87c548c9 100644 --- a/core/java/android/content/pm/parsing/component/ParsedPermission.java +++ b/core/java/android/content/pm/parsing/component/ParsedPermission.java @@ -21,16 +21,22 @@ import android.content.pm.PermissionInfo; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; +import android.util.ArraySet; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.DataClass; +import com.android.internal.util.Parcelling; import com.android.internal.util.Parcelling.BuiltIn.ForInternedString; +import com.android.internal.util.Parcelling.BuiltIn.ForStringSet; +import java.util.Locale; import java.util.Set; /** @hide */ public class ParsedPermission extends ParsedComponent { + private static ForStringSet sForStringSet = Parcelling.Cache.getOrCreate(ForStringSet.class); + @Nullable String backgroundPermission; @Nullable @@ -89,6 +95,19 @@ public class ParsedPermission extends ParsedComponent { return knownCerts; } + protected void setKnownCert(String knownCert) { + // Convert the provided digest to upper case for consistent Set membership + // checks when verifying the signing certificate digests of requesting apps. + this.knownCerts = Set.of(knownCert.toUpperCase(Locale.US)); + } + + protected void setKnownCerts(String[] knownCerts) { + this.knownCerts = new ArraySet<>(); + for (String knownCert : knownCerts) { + this.knownCerts.add(knownCert.toUpperCase(Locale.US)); + } + } + public int calculateFootprint() { int size = getName().length(); if (getNonLocalizedLabel() != null) { @@ -117,6 +136,7 @@ public class ParsedPermission extends ParsedComponent { dest.writeInt(this.protectionLevel); dest.writeBoolean(this.tree); dest.writeParcelable(this.parsedPermissionGroup, flags); + sForStringSet.parcel(knownCerts, dest, flags); } protected ParsedPermission(Parcel in) { @@ -129,6 +149,7 @@ public class ParsedPermission extends ParsedComponent { this.protectionLevel = in.readInt(); this.tree = in.readBoolean(); this.parsedPermissionGroup = in.readParcelable(boot); + this.knownCerts = sForStringSet.unparcel(in); } public static final Parcelable.Creator CREATOR = diff --git a/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java b/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java index a7cecbee8aec1..8afa70ec6364c 100644 --- a/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java +++ b/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java @@ -25,7 +25,6 @@ import android.content.pm.parsing.result.ParseResult; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; -import android.util.ArraySet; import android.util.Slog; import com.android.internal.R; @@ -33,8 +32,6 @@ import com.android.internal.R; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; -import java.util.Locale; -import java.util.Set; /** @hide */ public class ParsedPermissionUtils { @@ -103,17 +100,12 @@ public class ParsedPermissionUtils { if (resourceType.equals("array")) { final String[] knownCerts = res.getStringArray(knownCertsResource); if (knownCerts != null) { - // Convert the provided digest to upper case for consistent Set membership - // checks when verifying the signing certificate digests of requesting apps. - permission.knownCerts = new ArraySet<>(); - for (String knownCert : knownCerts) { - permission.knownCerts.add(knownCert.toUpperCase(Locale.US)); - } + permission.setKnownCerts(knownCerts); } } else { final String knownCert = res.getString(knownCertsResource); if (knownCert != null) { - permission.knownCerts = Set.of(knownCert.toUpperCase(Locale.US)); + permission.setKnownCert(knownCert); } } if (permission.knownCerts == null) { @@ -126,7 +118,7 @@ public class ParsedPermissionUtils { final String knownCert = sa.getString( R.styleable.AndroidManifestPermission_knownCerts); if (knownCert != null) { - permission.knownCerts = Set.of(knownCert.toUpperCase(Locale.US)); + permission.setKnownCert(knownCert); } } diff --git a/core/tests/coretests/src/android/content/pm/PermissionInfoTest.java b/core/tests/coretests/src/android/content/pm/PermissionInfoTest.java new file mode 100644 index 0000000000000..606e81d642895 --- /dev/null +++ b/core/tests/coretests/src/android/content/pm/PermissionInfoTest.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2021 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 android.content.pm; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import android.os.Parcel; +import android.util.ArraySet; + +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public final class PermissionInfoTest { + private static final String KNOWN_CERT_DIGEST_1 = + "6a8b96e278e58f62cfe3584022cec1d0527fcb85a9e5d2e1694eb0405be5b599"; + private static final String KNOWN_CERT_DIGEST_2 = + "9369370ffcfdc1e92dae777252c05c483b8cbb55fa9d5fd9f6317f623ae6d8c6"; + + @Test + public void createFromParcel_returnsKnownCerts() { + // The platform supports a knownSigner permission protection flag that allows one or more + // trusted signing certificates to be specified with the permission declaration; if a + // requesting app is signed by any of these trusted certificates the permission is granted. + // This test verifies the Set of knownCerts is properly parceled / unparceled. + PermissionInfo permissionInfo = new PermissionInfo(); + permissionInfo.protectionLevel = + PermissionInfo.PROTECTION_SIGNATURE | PermissionInfo.PROTECTION_FLAG_KNOWN_SIGNER; + permissionInfo.knownCerts = new ArraySet<>(2); + permissionInfo.knownCerts.add(KNOWN_CERT_DIGEST_1); + permissionInfo.knownCerts.add(KNOWN_CERT_DIGEST_2); + Parcel parcel = Parcel.obtain(); + permissionInfo.writeToParcel(parcel, 0); + + parcel.setDataPosition(0); + PermissionInfo unparceledPermissionInfo = PermissionInfo.CREATOR.createFromParcel(parcel); + + assertNotNull(unparceledPermissionInfo.knownCerts); + assertEquals(2, unparceledPermissionInfo.knownCerts.size()); + assertTrue(unparceledPermissionInfo.knownCerts.contains(KNOWN_CERT_DIGEST_1)); + assertTrue(unparceledPermissionInfo.knownCerts.contains(KNOWN_CERT_DIGEST_2)); + } +}