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

Commit 4db6bc16 authored by Patrick Baumann's avatar Patrick Baumann
Browse files

Increase instant app privacy with dummy data

This change adds some additional digest prefixes to instant app
resolution to make inverting the hash more difficult.

Change-Id: Ibf907495019338b5cac5dd22da275799e92f9b60
Fixes: 63445230
Test: manual - resolution still works with current production resolver
parent 3367c05b
Loading
Loading
Loading
Loading
+10 −0
Original line number Original line Diff line number Diff line
@@ -18,12 +18,14 @@ package android.content.pm;


import android.content.Intent;
import android.content.Intent;
import android.os.Bundle;
import android.os.Bundle;
import android.text.TextUtils;


/**
/**
 * Information needed to make an instant application resolution request.
 * Information needed to make an instant application resolution request.
 * @hide
 * @hide
 */
 */
public final class InstantAppRequest {
public final class InstantAppRequest {

    /** Response from the first phase of instant application resolution */
    /** Response from the first phase of instant application resolution */
    public final AuxiliaryResolveInfo responseObj;
    public final AuxiliaryResolveInfo responseObj;
    /** The original intent that triggered instant application resolution */
    /** The original intent that triggered instant application resolution */
@@ -40,6 +42,8 @@ public final class InstantAppRequest {
    public final Bundle verificationBundle;
    public final Bundle verificationBundle;
    /** Whether resolution occurs because an application is starting */
    /** Whether resolution occurs because an application is starting */
    public final boolean resolveForStart;
    public final boolean resolveForStart;
    /** The instant app digest for this request */
    public final InstantAppResolveInfo.InstantAppDigest digest;


    public InstantAppRequest(AuxiliaryResolveInfo responseObj, Intent origIntent,
    public InstantAppRequest(AuxiliaryResolveInfo responseObj, Intent origIntent,
            String resolvedType, String callingPackage, int userId, Bundle verificationBundle,
            String resolvedType, String callingPackage, int userId, Bundle verificationBundle,
@@ -51,5 +55,11 @@ public final class InstantAppRequest {
        this.userId = userId;
        this.userId = userId;
        this.verificationBundle = verificationBundle;
        this.verificationBundle = verificationBundle;
        this.resolveForStart = resolveForStart;
        this.resolveForStart = resolveForStart;
        if (origIntent.getData() != null && !TextUtils.isEmpty(origIntent.getData().getHost())) {
            digest = new InstantAppResolveInfo.InstantAppDigest(
                    origIntent.getData().getHost(), 5 /*maxDigests*/);
        } else {
            digest = InstantAppResolveInfo.InstantAppDigest.UNDEFINED;
        }
    }
    }
}
}
+47 −3
Original line number Original line Diff line number Diff line
@@ -26,10 +26,13 @@ import android.os.Parcelable;


import java.security.MessageDigest;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Collections;
import java.util.List;
import java.util.List;
import java.util.Locale;
import java.util.Locale;
import java.util.Random;


/**
/**
 * Describes an externally resolvable instant application. There are three states that this class
 * Describes an externally resolvable instant application. There are three states that this class
@@ -227,14 +230,25 @@ public final class InstantAppResolveInfo implements Parcelable {
     */
     */
    @SystemApi
    @SystemApi
    public static final class InstantAppDigest implements Parcelable {
    public static final class InstantAppDigest implements Parcelable {
        private static final int DIGEST_MASK = 0xfffff000;
        static final int DIGEST_MASK = 0xfffff000;

        public static final InstantAppDigest UNDEFINED =
        public static final InstantAppDigest UNDEFINED =
                new InstantAppDigest(new byte[][]{}, new int[]{});
                new InstantAppDigest(new byte[][]{}, new int[]{});

        private static Random sRandom = null;
        static {
            try {
                sRandom = SecureRandom.getInstance("SHA1PRNG");
            } catch (NoSuchAlgorithmException e) {
                // oh well
                sRandom = new Random();
            }
        }
        /** Full digest of the domain hashes */
        /** Full digest of the domain hashes */
        private final byte[][] mDigestBytes;
        private final byte[][] mDigestBytes;
        /** The first 4 bytes of the domain hashes */
        /** The first 5 bytes of the domain hashes */
        private final int[] mDigestPrefix;
        private final int[] mDigestPrefix;
        /** The first 5 bytes of the domain hashes interspersed with random data */
        private int[] mDigestPrefixSecure;


        public InstantAppDigest(@NonNull String hostName) {
        public InstantAppDigest(@NonNull String hostName) {
            this(hostName, -1 /*maxDigests*/);
            this(hostName, -1 /*maxDigests*/);
@@ -306,6 +320,7 @@ public final class InstantAppResolveInfo implements Parcelable {
                }
                }
            }
            }
            mDigestPrefix = in.createIntArray();
            mDigestPrefix = in.createIntArray();
            mDigestPrefixSecure = in.createIntArray();
        }
        }


        public byte[][] getDigestBytes() {
        public byte[][] getDigestBytes() {
@@ -316,6 +331,26 @@ public final class InstantAppResolveInfo implements Parcelable {
            return mDigestPrefix;
            return mDigestPrefix;
        }
        }


        /**
         * Returns a digest prefix with additional random prefixes interspersed.
         * @hide
         */
        public int[] getDigestPrefixSecure() {
            if (this == InstantAppResolveInfo.InstantAppDigest.UNDEFINED) {
                return getDigestPrefix();
            } else if (mDigestPrefixSecure == null) {
                // let's generate some random data to intersperse throughout the set of prefixes
                final int realSize = getDigestPrefix().length;
                final int manufacturedSize = realSize + 10 + sRandom.nextInt(10);
                mDigestPrefixSecure = Arrays.copyOf(getDigestPrefix(), manufacturedSize);
                for (int i = realSize; i < manufacturedSize; i++) {
                    mDigestPrefixSecure[i] = sRandom.nextInt() & DIGEST_MASK;
                }
                Arrays.sort(mDigestPrefixSecure);
            }
            return mDigestPrefixSecure;
        }

        @Override
        @Override
        public int describeContents() {
        public int describeContents() {
            return 0;
            return 0;
@@ -323,6 +358,11 @@ public final class InstantAppResolveInfo implements Parcelable {


        @Override
        @Override
        public void writeToParcel(Parcel out, int flags) {
        public void writeToParcel(Parcel out, int flags) {
            final boolean isUndefined = this == UNDEFINED;
            out.writeBoolean(isUndefined);
            if (isUndefined) {
                return;
            }
            if (mDigestBytes == null) {
            if (mDigestBytes == null) {
                out.writeInt(-1);
                out.writeInt(-1);
            } else {
            } else {
@@ -332,6 +372,7 @@ public final class InstantAppResolveInfo implements Parcelable {
                }
                }
            }
            }
            out.writeIntArray(mDigestPrefix);
            out.writeIntArray(mDigestPrefix);
            out.writeIntArray(mDigestPrefixSecure);
        }
        }


        @SuppressWarnings("hiding")
        @SuppressWarnings("hiding")
@@ -339,6 +380,9 @@ public final class InstantAppResolveInfo implements Parcelable {
                new Parcelable.Creator<InstantAppDigest>() {
                new Parcelable.Creator<InstantAppDigest>() {
            @Override
            @Override
            public InstantAppDigest createFromParcel(Parcel in) {
            public InstantAppDigest createFromParcel(Parcel in) {
                if (in.readBoolean() /* is undefined */) {
                    return UNDEFINED;
                }
                return new InstantAppDigest(in);
                return new InstantAppDigest(in);
            }
            }
            @Override
            @Override
+8 −15
Original line number Original line Diff line number Diff line
@@ -126,17 +126,16 @@ public abstract class InstantAppResolver {
        final Intent origIntent = requestObj.origIntent;
        final Intent origIntent = requestObj.origIntent;
        final Intent sanitizedIntent = sanitizeIntent(origIntent);
        final Intent sanitizedIntent = sanitizeIntent(origIntent);


        final InstantAppDigest digest = getInstantAppDigest(origIntent);
        final int[] shaPrefix = digest.getDigestPrefix();
        AuxiliaryResolveInfo resolveInfo = null;
        AuxiliaryResolveInfo resolveInfo = null;
        @ResolutionStatus int resolutionStatus = RESOLUTION_SUCCESS;
        @ResolutionStatus int resolutionStatus = RESOLUTION_SUCCESS;
        try {
        try {
            final List<InstantAppResolveInfo> instantAppResolveInfoList =
            final List<InstantAppResolveInfo> instantAppResolveInfoList =
                    connection.getInstantAppResolveInfoList(sanitizedIntent, shaPrefix, token);
                    connection.getInstantAppResolveInfoList(sanitizedIntent,
                            requestObj.digest.getDigestPrefixSecure(), token);
            if (instantAppResolveInfoList != null && instantAppResolveInfoList.size() > 0) {
            if (instantAppResolveInfoList != null && instantAppResolveInfoList.size() > 0) {
                resolveInfo = InstantAppResolver.filterInstantAppIntent(
                resolveInfo = InstantAppResolver.filterInstantAppIntent(
                        instantAppResolveInfoList, origIntent, requestObj.resolvedType,
                        instantAppResolveInfoList, origIntent, requestObj.resolvedType,
                        requestObj.userId, origIntent.getPackage(), digest, token);
                        requestObj.userId, origIntent.getPackage(), requestObj.digest, token);
            }
            }
        } catch (ConnectionException e) {
        } catch (ConnectionException e) {
            if (e.failure == ConnectionException.FAILURE_BIND) {
            if (e.failure == ConnectionException.FAILURE_BIND) {
@@ -166,12 +165,6 @@ public abstract class InstantAppResolver {
        return resolveInfo;
        return resolveInfo;
    }
    }


    private static InstantAppDigest getInstantAppDigest(Intent origIntent) {
        return origIntent.getData() != null && !TextUtils.isEmpty(origIntent.getData().getHost())
                ? new InstantAppDigest(origIntent.getData().getHost(), 5 /*maxDigests*/)
                : InstantAppDigest.UNDEFINED;
    }

    public static void doInstantAppResolutionPhaseTwo(Context context,
    public static void doInstantAppResolutionPhaseTwo(Context context,
            InstantAppResolverConnection connection, InstantAppRequest requestObj,
            InstantAppResolverConnection connection, InstantAppRequest requestObj,
            ActivityInfo instantAppInstaller, Handler callbackHandler) {
            ActivityInfo instantAppInstaller, Handler callbackHandler) {
@@ -182,8 +175,6 @@ public abstract class InstantAppResolver {
        }
        }
        final Intent origIntent = requestObj.origIntent;
        final Intent origIntent = requestObj.origIntent;
        final Intent sanitizedIntent = sanitizeIntent(origIntent);
        final Intent sanitizedIntent = sanitizeIntent(origIntent);
        final InstantAppDigest digest = getInstantAppDigest(origIntent);
        final int[] shaPrefix = digest.getDigestPrefix();


        final PhaseTwoCallback callback = new PhaseTwoCallback() {
        final PhaseTwoCallback callback = new PhaseTwoCallback() {
            @Override
            @Override
@@ -194,7 +185,8 @@ public abstract class InstantAppResolver {
                    final AuxiliaryResolveInfo instantAppIntentInfo =
                    final AuxiliaryResolveInfo instantAppIntentInfo =
                            InstantAppResolver.filterInstantAppIntent(
                            InstantAppResolver.filterInstantAppIntent(
                                    instantAppResolveInfoList, origIntent, null /*resolvedType*/,
                                    instantAppResolveInfoList, origIntent, null /*resolvedType*/,
                                    0 /*userId*/, origIntent.getPackage(), digest, token);
                                    0 /*userId*/, origIntent.getPackage(), requestObj.digest,
                                    token);
                    if (instantAppIntentInfo != null) {
                    if (instantAppIntentInfo != null) {
                        failureIntent = instantAppIntentInfo.failureIntent;
                        failureIntent = instantAppIntentInfo.failureIntent;
                    } else {
                    } else {
@@ -225,8 +217,9 @@ public abstract class InstantAppResolver {
            }
            }
        };
        };
        try {
        try {
            connection.getInstantAppIntentFilterList(sanitizedIntent, shaPrefix, token, callback,
            connection.getInstantAppIntentFilterList(sanitizedIntent,
                    callbackHandler, startTime);
                    requestObj.digest.getDigestPrefixSecure(), token, callback, callbackHandler,
                    startTime);
        } catch (ConnectionException e) {
        } catch (ConnectionException e) {
            @ResolutionStatus int resolutionStatus = RESOLUTION_FAILURE;
            @ResolutionStatus int resolutionStatus = RESOLUTION_FAILURE;
            if (e.failure == ConnectionException.FAILURE_BIND) {
            if (e.failure == ConnectionException.FAILURE_BIND) {