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

Commit b31ab674 authored by Bo Zhu's avatar Bo Zhu
Browse files

Use the new root cert file under the core/ folder

This CL also adds an alias param to the RecoverySession#start method.

Bug: 76033708
Test: runtest frameworks-services -p \
      com.android.server.locksettings.recoverablekeystore

Change-Id: I870f4f89bd6e319e1687a981aa04af0d23f3c922
parent 8512630f
Loading
Loading
Loading
Loading
+57 −0
Original line number Diff line number Diff line
@@ -136,6 +136,63 @@ public class RecoverySession implements AutoCloseable {
            byte[] recoveryClaim =
                    mRecoveryController.getBinder().startRecoverySessionWithCertPath(
                            mSessionId,
                            /*rootCertificateAlias=*/ "",  // Use the default root cert
                            recoveryCertPath,
                            vaultParams,
                            vaultChallenge,
                            secrets);
            return recoveryClaim;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        } catch (ServiceSpecificException e) {
            if (e.errorCode == RecoveryController.ERROR_BAD_CERTIFICATE_FORMAT
                    || e.errorCode == RecoveryController.ERROR_INVALID_CERTIFICATE) {
                throw new CertificateException(e.getMessage());
            }
            throw mRecoveryController.wrapUnexpectedServiceSpecificException(e);
        }
    }

    /**
     * Starts a recovery session and returns a blob with proof of recovery secret possession.
     * The method generates a symmetric key for a session, which trusted remote device can use to
     * return recovery key.
     *
     * @param rootCertificateAlias The alias of the root certificate that is already in the Android
     *     OS. The root certificate will be used for validating {@code verifierCertPath}.
     * @param verifierCertPath The certificate path used to create the recovery blob on the source
     *     device. Keystore will verify the certificate path by using the root of trust.
     * @param vaultParams Must match the parameters in the corresponding field in the recovery blob.
     *     Used to limit number of guesses.
     * @param vaultChallenge Data passed from server for this recovery session and used to prevent
     *     replay attacks.
     * @param secrets Secrets provided by user, the method only uses type and secret fields.
     * @return The recovery claim. Claim provides a b binary blob with recovery claim. It is
     *     encrypted with verifierPublicKey and contains a proof of user secrets, session symmetric
     *     key and parameters necessary to identify the counter with the number of failed recovery
     *     attempts.
     * @throws CertificateException if the {@code verifierCertPath} is invalid.
     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
     *     service.
     *
     * @hide
     */
    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
    @NonNull public byte[] start(
            @NonNull String rootCertificateAlias,
            @NonNull CertPath verifierCertPath,
            @NonNull byte[] vaultParams,
            @NonNull byte[] vaultChallenge,
            @NonNull List<KeyChainProtectionParams> secrets)
            throws CertificateException, InternalRecoveryServiceException {
        // Wrap the CertPath in a Parcelable so it can be passed via Binder calls.
        RecoveryCertPath recoveryCertPath =
                RecoveryCertPath.createRecoveryCertPath(verifierCertPath);
        try {
            byte[] recoveryClaim =
                    mRecoveryController.getBinder().startRecoverySessionWithCertPath(
                            mSessionId,
                            rootCertificateAlias,
                            recoveryCertPath,
                            vaultParams,
                            vaultChallenge,
+17 −0
Original line number Diff line number Diff line
@@ -77,10 +77,27 @@ public class TrustedRootCertificates {

    private static final int NUMBER_OF_ROOT_CERTIFICATES = 1;

    private static final ArrayMap<String, X509Certificate> ALL_ROOT_CERTIFICATES =
            constructRootCertificateMap();

    /**
     * Returns all available root certificates, keyed by alias.
     */
    public static Map<String, X509Certificate> listRootCertificates() {
        return new ArrayMap(ALL_ROOT_CERTIFICATES);
    }

    /**
     * Gets a root certificate referenced by the given {@code alias}.
     *
     * @param alias the alias of the certificate
     * @return the certificate referenced by the alias, or null if such a certificate doesn't exist.
     */
    public static X509Certificate getRootCertificate(String alias) {
        return ALL_ROOT_CERTIFICATES.get(alias);
    }

    private static ArrayMap<String, X509Certificate> constructRootCertificateMap() {
        ArrayMap<String, X509Certificate> certificates =
                new ArrayMap<>(NUMBER_OF_ROOT_CERTIFICATES);
        certificates.put(
+1 −1
Original line number Diff line number Diff line
@@ -78,7 +78,7 @@ interface ILockSettings {
    byte[] startRecoverySession(in String sessionId,
            in byte[] verifierPublicKey, in byte[] vaultParams, in byte[] vaultChallenge,
            in List<KeyChainProtectionParams> secrets);
    byte[] startRecoverySessionWithCertPath(in String sessionId,
    byte[] startRecoverySessionWithCertPath(in String sessionId, in String rootCertificateAlias,
            in RecoveryCertPath verifierCertPath, in byte[] vaultParams, in byte[] vaultChallenge,
            in List<KeyChainProtectionParams> secrets);
    Map/*<String, byte[]>*/ recoverKeys(in String sessionId, in byte[] recoveryKeyBlob,
+5 −3
Original line number Diff line number Diff line
@@ -2051,11 +2051,13 @@ public class LockSettingsService extends ILockSettings.Stub {

    @Override
    public byte[] startRecoverySessionWithCertPath(@NonNull String sessionId,
            @NonNull RecoveryCertPath verifierCertPath, @NonNull byte[] vaultParams,
            @NonNull byte[] vaultChallenge, @NonNull List<KeyChainProtectionParams> secrets)
            @NonNull String rootCertificateAlias, @NonNull RecoveryCertPath verifierCertPath,
            @NonNull byte[] vaultParams, @NonNull byte[] vaultChallenge,
            @NonNull List<KeyChainProtectionParams> secrets)
            throws RemoteException {
        return mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
                sessionId, verifierCertPath, vaultParams, vaultChallenge, secrets);
                sessionId, rootCertificateAlias, verifierCertPath, vaultParams, vaultChallenge,
                secrets);
    }

    public void closeSession(@NonNull String sessionId) throws RemoteException {
+27 −6
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@ import android.security.keystore.recovery.KeyChainProtectionParams;
import android.security.keystore.recovery.KeyChainSnapshot;
import android.security.keystore.recovery.RecoveryCertPath;
import android.security.keystore.recovery.RecoveryController;
import android.security.keystore.recovery.TrustedRootCertificates;
import android.security.keystore.recovery.WrappedApplicationKey;
import android.security.KeyStore;
import android.util.Log;
@@ -50,7 +51,6 @@ import com.android.server.locksettings.recoverablekeystore.storage.ApplicationKe
import com.android.server.locksettings.recoverablekeystore.certificate.CertParsingException;
import com.android.server.locksettings.recoverablekeystore.certificate.CertValidationException;
import com.android.server.locksettings.recoverablekeystore.certificate.CertXml;
import com.android.server.locksettings.recoverablekeystore.certificate.TrustedRootCert;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverySessionStorage;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverySnapshotStorage;
@@ -64,6 +64,7 @@ import java.security.UnrecoverableKeyException;
import java.security.cert.CertPath;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
@@ -200,15 +201,19 @@ public class RecoverableKeyStoreManager {
        }
        Log.i(TAG, "Updating the certificate with the new serial number " + newSerial);

        // Randomly choose and validate an endpoint certificate from the list
        CertPath certPath;
        X509Certificate rootCert = getRootCertificate(rootCertificateAlias);
        try {
            Log.d(TAG, "Getting and validating a random endpoint certificate");
            certPath = certXml.getRandomEndpointCert(TrustedRootCert.TRUSTED_ROOT_CERT);
            certPath = certXml.getRandomEndpointCert(rootCert);
        } catch (CertValidationException e) {
            Log.e(TAG, "Invalid endpoint cert", e);
            throw new ServiceSpecificException(
                    ERROR_INVALID_CERTIFICATE, "Failed to validate certificate.");
        }

        // Save the chosen and validated certificate into database
        try {
            Log.d(TAG, "Saving the randomly chosen endpoint certificate to database");
            if (mDatabase.setRecoveryServiceCertPath(userId, uid, certPath) > 0) {
@@ -253,8 +258,9 @@ public class RecoverableKeyStoreManager {
                    ERROR_BAD_CERTIFICATE_FORMAT, "Failed to parse the sig file.");
        }

        X509Certificate rootCert = getRootCertificate(rootCertificateAlias);
        try {
            sigXml.verifyFileSignature(TrustedRootCert.TRUSTED_ROOT_CERT, recoveryServiceCertFile);
            sigXml.verifyFileSignature(rootCert, recoveryServiceCertFile);
        } catch (CertValidationException e) {
            Log.d(TAG, "The signature over the cert file is invalid."
                    + " Cert: " + HexDump.toHexString(recoveryServiceCertFile)
@@ -479,6 +485,7 @@ public class RecoverableKeyStoreManager {
     */
    public @NonNull byte[] startRecoverySessionWithCertPath(
            @NonNull String sessionId,
            @NonNull String rootCertificateAlias,
            @NonNull RecoveryCertPath verifierCertPath,
            @NonNull byte[] vaultParams,
            @NonNull byte[] vaultChallenge,
@@ -495,11 +502,10 @@ public class RecoverableKeyStoreManager {
        }

        try {
            CertUtils.validateCertPath(TrustedRootCert.TRUSTED_ROOT_CERT, certPath);
            CertUtils.validateCertPath(getRootCertificate(rootCertificateAlias), certPath);
        } catch (CertValidationException e) {
            Log.e(TAG, "Failed to validate the given cert path", e);
            // TODO: Change this to ERROR_INVALID_CERTIFICATE once ag/3666620 is submitted
            throw new ServiceSpecificException(ERROR_BAD_CERTIFICATE_FORMAT, e.getMessage());
            throw new ServiceSpecificException(ERROR_INVALID_CERTIFICATE, e.getMessage());
        }

        byte[] verifierPublicKey = certPath.getCertificates().get(0).getPublicKey().getEncoded();
@@ -837,6 +843,21 @@ public class RecoverableKeyStoreManager {
        }
    }

    private X509Certificate getRootCertificate(String rootCertificateAlias) throws RemoteException {
        if (rootCertificateAlias == null || rootCertificateAlias.isEmpty()) {
            // Use the default Google Key Vault Service CA certificate if the alias is not provided
            rootCertificateAlias = TrustedRootCertificates.GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_ALIAS;
        }

        X509Certificate rootCertificate =
                TrustedRootCertificates.getRootCertificate(rootCertificateAlias);
        if (rootCertificate == null) {
            throw new ServiceSpecificException(
                    ERROR_INVALID_CERTIFICATE, "The provided root certificate alias is invalid");
        }
        return rootCertificate;
    }

    private void checkRecoverKeyStorePermission() {
        mContext.enforceCallingOrSelfPermission(
                Manifest.permission.RECOVER_KEYSTORE,
Loading