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

Commit ebe3ba87 authored by Shawn Willden's avatar Shawn Willden
Browse files

Harden Factory Reset Protection

This change modifies PersistentDataBlockService to track the factory
reset protection state, to block updating the data block when FRP is
active (meaning there may have been an untrusted reset), and to
require presentation of a secret to deactivate FRP.

Bug: 290312729
Test: atest PersistentDataBlockTest
Change-Id: Ibfd053dc07bdea947e3043fc13bb17a7f5a986e9
parent dd47c034
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -40960,6 +40960,14 @@ package android.service.notification {
}
package android.service.persistentdata {
  @FlaggedApi("android.security.frp_enforcement") public class PersistentDataBlockManager {
    method @FlaggedApi("android.security.frp_enforcement") public boolean isFactoryResetProtectionActive();
  }
}
package android.service.quickaccesswallet {
  public interface GetWalletCardsCallback {
+4 −1
Original line number Diff line number Diff line
@@ -112,6 +112,7 @@ package android {
    field public static final String CLEAR_APP_USER_DATA = "android.permission.CLEAR_APP_USER_DATA";
    field public static final String COMPANION_APPROVE_WIFI_CONNECTIONS = "android.permission.COMPANION_APPROVE_WIFI_CONNECTIONS";
    field public static final String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS";
    field @FlaggedApi("android.security.frp_enforcement") public static final String CONFIGURE_FACTORY_RESET_PROTECTION = "android.permission.CONFIGURE_FACTORY_RESET_PROTECTION";
    field public static final String CONFIGURE_INTERACT_ACROSS_PROFILES = "android.permission.CONFIGURE_INTERACT_ACROSS_PROFILES";
    field @Deprecated public static final String CONNECTIVITY_INTERNAL = "android.permission.CONNECTIVITY_INTERNAL";
    field public static final String CONNECTIVITY_USE_RESTRICTED_NETWORKS = "android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS";
@@ -12672,13 +12673,15 @@ package android.service.oemlock {
package android.service.persistentdata {
  public class PersistentDataBlockManager {
  @FlaggedApi("android.security.frp_enforcement") public class PersistentDataBlockManager {
    method @FlaggedApi("android.security.frp_enforcement") @RequiresPermission(android.Manifest.permission.CONFIGURE_FACTORY_RESET_PROTECTION) public boolean deactivateFactoryResetProtection(@NonNull byte[]);
    method @RequiresPermission(android.Manifest.permission.ACCESS_PDB_STATE) public int getDataBlockSize();
    method @RequiresPermission(anyOf={android.Manifest.permission.READ_OEM_UNLOCK_STATE, "android.permission.OEM_UNLOCK_STATE"}) public int getFlashLockState();
    method public long getMaximumDataBlockSize();
    method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_OEM_UNLOCK_STATE, "android.permission.OEM_UNLOCK_STATE"}) public boolean getOemUnlockEnabled();
    method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_PDB_STATE) public String getPersistentDataPackageName();
    method public byte[] read();
    method @FlaggedApi("android.security.frp_enforcement") public boolean setFactoryResetProtectionSecret(@NonNull byte[]);
    method @Deprecated @RequiresPermission("android.permission.OEM_UNLOCK_STATE") public void setOemUnlockEnabled(boolean);
    method @RequiresPermission("android.permission.OEM_UNLOCK_STATE") public void wipe();
    method public int write(byte[]);
+29 −1
Original line number Diff line number Diff line
@@ -38,5 +38,33 @@ interface IPersistentDataBlockService {
    int getFlashLockState();
    boolean hasFrpCredentialHandle();
    String getPersistentDataPackageName();
}

    /**
     * Returns true if Factory Reset Protection (FRP) is active, meaning the device rebooted and has
     * not been able to transition to the FRP inactive state.
     */
    boolean isFactoryResetProtectionActive();

    /**
     * Attempts to deactivate Factory Reset Protection (FRP) with the provided secret.  If the
     * provided secret matches the stored FRP secret, FRP is deactivated and the method returns
     * true.  Otherwise, FRP state remains unchanged and the method returns false.
     */
    boolean deactivateFactoryResetProtection(in byte[] secret);

    /**
     * Stores the provided Factory Reset Protection (FRP) secret as the secret to be used for future
     * FRP deactivation.  The secret must be 32 bytes in length.  Setting the all-zeros "default"
     * value disables the FRP feature entirely.
     *
     * It's the responsibility of the caller to ensure that copies of the FRP secret are stored
     * securely where they can be recovered and used to deactivate FRP after an untrusted reset.
     * This method will store a copy in /data/system and use that to automatically deactivate FRP
     * until /data is wiped.
     *
     * Note that this method does nothing if FRP is currently active.
     *
     * Returns true if the secret was successfully changed, false otherwise.
     */
    boolean setFactoryResetProtectionSecret(in byte[] secret);
}
+118 −19
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.service.persistentdata;

import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
@@ -24,30 +25,17 @@ import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
import android.os.RemoteException;
import android.security.Flags;
import android.service.oemlock.OemLockManager;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * Interface for reading and writing data blocks to a persistent partition.
 *
 * Allows writing one block at a time. Namely, each time
 * {@link PersistentDataBlockManager#write(byte[])}
 * is called, it will overwite the data that was previously written on the block.
 *
 * Clients can query the size of the currently written block via
 * {@link PersistentDataBlockManager#getDataBlockSize()}.
 *
 * Clients can query the maximum size for a block via
 * {@link PersistentDataBlockManager#getMaximumDataBlockSize()}
 *
 * Clients can read the currently written block by invoking
 * {@link PersistentDataBlockManager#read()}.
 *
 * @hide
 * Interface to the persistent data partition.  Provides access to information about the state
 * of factory reset protection.
 */
@SystemApi
@FlaggedApi(Flags.FLAG_FRP_ENFORCEMENT)
@SystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE)
public class PersistentDataBlockManager {
    private static final String TAG = PersistentDataBlockManager.class.getSimpleName();
@@ -55,18 +43,32 @@ public class PersistentDataBlockManager {

    /**
     * Indicates that the device's bootloader lock state is UNKNOWN.
     *
     * @hide
     */
    @SystemApi
    public static final int FLASH_LOCK_UNKNOWN = -1;
    /**
     * Indicates that the device's bootloader is UNLOCKED.
     *
     * @hide
     */
    @SystemApi
    public static final int FLASH_LOCK_UNLOCKED = 0;
    /**
     * Indicates that the device's bootloader is LOCKED.
     *
     * @hide
     */
    @SystemApi
    public static final int FLASH_LOCK_LOCKED = 1;

    /** @removed mistakenly exposed previously */
    /**
     * @removed mistakenly exposed previously
     *
     * @hide
     */
    @SystemApi
    @IntDef(prefix = { "FLASH_LOCK_" }, value = {
            FLASH_LOCK_UNKNOWN,
            FLASH_LOCK_LOCKED,
@@ -75,7 +77,9 @@ public class PersistentDataBlockManager {
    @Retention(RetentionPolicy.SOURCE)
    public @interface FlashLockState {}

    /** @hide */
    /**
     * @hide
     */
    public PersistentDataBlockManager(IPersistentDataBlockService service) {
        sService = service;
    }
@@ -91,7 +95,10 @@ public class PersistentDataBlockManager {
     * in which case -1 will be returned.
     *
     * @param data the data to write
     *
     * @hide
     */
    @SystemApi
    @SuppressLint("RequiresPermission")
    public int write(byte[] data) {
        try {
@@ -103,7 +110,10 @@ public class PersistentDataBlockManager {

    /**
     * Returns the data block stored on the persistent partition.
     *
     * @hide
     */
    @SystemApi
    @SuppressLint("RequiresPermission")
    public byte[] read() {
        try {
@@ -117,7 +127,10 @@ public class PersistentDataBlockManager {
     * Retrieves the size of the block currently written to the persistent partition.
     *
     * Return -1 on error.
     *
     * @hide
     */
    @SystemApi
    @RequiresPermission(android.Manifest.permission.ACCESS_PDB_STATE)
    public int getDataBlockSize() {
        try {
@@ -131,7 +144,10 @@ public class PersistentDataBlockManager {
     * Retrieves the maximum size allowed for a data block.
     *
     * Returns -1 on error.
     *
     * @hide
     */
    @SystemApi
    @SuppressLint("RequiresPermission")
    public long getMaximumDataBlockSize() {
        try {
@@ -146,7 +162,10 @@ public class PersistentDataBlockManager {
     * will erase all data written to the persistent data partition.
     * It will also prevent any further {@link #write} operation until reboot,
     * in order to prevent a potential race condition. See b/30352311.
     *
     * @hide
     */
    @SystemApi
    @RequiresPermission(android.Manifest.permission.OEM_UNLOCK_STATE)
    public void wipe() {
        try {
@@ -160,7 +179,11 @@ public class PersistentDataBlockManager {
     * Writes a byte enabling or disabling the ability to "OEM unlock" the device.
     *
     * @deprecated use {@link OemLockManager#setOemUnlockAllowedByUser(boolean)} instead.
     *
     * @hide
     */
    @SystemApi
    @Deprecated
    @RequiresPermission(android.Manifest.permission.OEM_UNLOCK_STATE)
    public void setOemUnlockEnabled(boolean enabled) {
        try {
@@ -174,7 +197,11 @@ public class PersistentDataBlockManager {
     * Returns whether or not "OEM unlock" is enabled or disabled on this device.
     *
     * @deprecated use {@link OemLockManager#isOemUnlockAllowedByUser()} instead.
     *
     * @hide
     */
    @SystemApi
    @Deprecated
    @RequiresPermission(anyOf = {
            android.Manifest.permission.READ_OEM_UNLOCK_STATE,
            android.Manifest.permission.OEM_UNLOCK_STATE
@@ -193,7 +220,10 @@ public class PersistentDataBlockManager {
     * @return {@link #FLASH_LOCK_LOCKED} if device bootloader is locked,
     * {@link #FLASH_LOCK_UNLOCKED} if device bootloader is unlocked, or {@link #FLASH_LOCK_UNKNOWN}
     * if this information cannot be ascertained on this device.
     *
     * @hide
     */
    @SystemApi
    @RequiresPermission(anyOf = {
            android.Manifest.permission.READ_OEM_UNLOCK_STATE,
            android.Manifest.permission.OEM_UNLOCK_STATE
@@ -222,4 +252,73 @@ public class PersistentDataBlockManager {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Returns true if FactoryResetProtection (FRP) is active, meaning the device rebooted and has
     * not been able to deactivate FRP because the deactivation secrets were wiped by an untrusted
     * factory reset.
     */
    @FlaggedApi(Flags.FLAG_FRP_ENFORCEMENT)
    public boolean isFactoryResetProtectionActive() {
        try {
            return sService.isFactoryResetProtectionActive();
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Attempt to deactivate FRP with the provided secret.  If the provided secret matches the
     * stored FRP secret, FRP is deactivated and the method returns true.  Otherwise, FRP state
     * remains unchanged and the method returns false.
     *
     * @hide
     */
    @FlaggedApi(Flags.FLAG_FRP_ENFORCEMENT)
    @SystemApi
    @RequiresPermission(android.Manifest.permission.CONFIGURE_FACTORY_RESET_PROTECTION)
    public boolean deactivateFactoryResetProtection(@NonNull byte[] secret) {
        try {
            return sService.deactivateFactoryResetProtection(secret);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Store the provided FRP secret as the secret to be used for future FRP deactivation.  The
     * secret must be 32 bytes in length.  Setting the all-zeros "default" value disables the FRP
     * feature entirely.
     *
     * To ensure that the device doesn't end up in a bad state if a crash occurs, this method
     * should be used in a three-step process:
     *
     * 1.  Generate a new secret and securely store any necessary copies (e.g. by encrypting them
     *     and calling #write with a new data block that contains both the old encrypted secret
     *     copies and the new ones).
     * 2.  Call this method to set the new FRP secret.  This will also write the copy used during
     *     normal boot.
     * 3.  Delete any old FRP secret copies (e.g. by calling #write with a new data block that
     *     contains only the new encrypted secret copies).
     *
     * Note that this method does nothing if FRP is currently active.
     *
     * This method does not require any permission, but can be called only by the
     * PersistentDataBlockService's authorized caller UID.
     *
     * Returns true if the new secret was successfully written.  Returns false if FRP is currently
     * active.
     *
     * @hide
     */
    @FlaggedApi(Flags.FLAG_FRP_ENFORCEMENT)
    @SystemApi
    @SuppressLint("RequiresPermission")
    public boolean setFactoryResetProtectionSecret(@NonNull byte[] secret) {
        try {
            return sService.setFactoryResetProtectionSecret(secret);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
}
+6 −0
Original line number Diff line number Diff line
@@ -2772,6 +2772,12 @@
    <permission android:name="android.permission.OEM_UNLOCK_STATE"
        android:protectionLevel="signature" />

    <!-- @SystemApi Allows configuration of factory reset protection
         @FlaggedApi("android.security.frp_enforcement")
         @hide <p>Not for use by third-party applications. -->
    <permission android:name="android.permission.CONFIGURE_FACTORY_RESET_PROTECTION"
        android:protectionLevel="signature|privileged" />

    <!-- @SystemApi @hide Allows querying state of PersistentDataBlock
   <p>Not for use by third-party applications. -->
    <permission android:name="android.permission.ACCESS_PDB_STATE"
Loading