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

Commit 82235880 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Use the new root cert file under the core/ folder" into pi-dev

parents 69517169 b31ab674
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