Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7c001ed2 authored by Michael Groover's avatar Michael Groover
Browse files

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
parent 79633c2b
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -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);
    }
}
+21 −0
Original line number Diff line number Diff line
@@ -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<ParsedPermission> CREATOR =
+3 −11
Original line number Diff line number Diff line
@@ -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);
                }
            }

+61 −0
Original line number Diff line number Diff line
/*
 * 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));
    }
}