Loading services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java +8 −5 Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.HexDump; import com.android.server.locksettings.recoverablekeystore.certificate.CertUtils; import com.android.server.locksettings.recoverablekeystore.storage.ApplicationKeyStorage; import com.android.server.locksettings.recoverablekeystore.certificate.CertParsingException; import com.android.server.locksettings.recoverablekeystore.certificate.CertValidationException; Loading Loading @@ -446,12 +447,14 @@ public class RecoverableKeyStoreManager { "Failed decode the certificate path"); } // TODO: Validate the cert path according to the root of trust if (certPath.getCertificates().isEmpty()) { throw new ServiceSpecificException(ERROR_BAD_CERTIFICATE_FORMAT, "The given CertPath is empty"); try { CertUtils.validateCertPath(TrustedRootCert.TRUSTED_ROOT_CERT, 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()); } byte[] verifierPublicKey = certPath.getCertificates().get(0).getPublicKey().getEncoded(); if (verifierPublicKey == null) { Log.e(TAG, "Failed to encode verifierPublicKey"); Loading services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertUtils.java +37 −0 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ import java.security.cert.CertPathBuilderException; import java.security.cert.CertPathValidator; import java.security.cert.CertPathValidatorException; import java.security.cert.CertStore; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.CollectionCertStoreParameters; Loading Loading @@ -292,6 +293,42 @@ public final class CertUtils { return certPath; } /** * Validates a given {@code CertPath} against the trusted root certificate. * * @param trustedRoot the trusted root certificate * @param certPath the certificate path to be validated * @throws CertValidationException if the given certificate path is invalid, e.g., is expired, * or does not have a valid signature */ public static void validateCertPath(X509Certificate trustedRoot, CertPath certPath) throws CertValidationException { validateCertPath(/*validationDate=*/ null, trustedRoot, certPath); } /** * Validates a given {@code CertPath} against a given {@code validationDate}. If the given * validation date is null, the current date will be used. */ @VisibleForTesting static void validateCertPath(@Nullable Date validationDate, X509Certificate trustedRoot, CertPath certPath) throws CertValidationException { if (certPath.getCertificates().isEmpty()) { throw new CertValidationException("The given certificate path is empty"); } if (!(certPath.getCertificates().get(0) instanceof X509Certificate)) { throw new CertValidationException( "The given certificate path does not contain X509 certificates"); } List<X509Certificate> certificates = (List<X509Certificate>) certPath.getCertificates(); X509Certificate leafCert = certificates.get(0); List<X509Certificate> intermediateCerts = certificates.subList(/*fromIndex=*/ 1, certificates.size()); validateCert(validationDate, trustedRoot, intermediateCerts, leafCert); } @VisibleForTesting static CertPath buildCertPath(PKIXParameters pkixParams) throws CertValidationException { CertPathBuilder certPathBuilder; Loading services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java +26 −2 Original line number Diff line number Diff line Loading @@ -277,7 +277,7 @@ public class RecoverableKeyStoreManagerTest { } @Test public void initRecoveryService_succeeds() throws Exception { public void initRecoveryService_succeedsWithCertFile() throws Exception { int uid = Binder.getCallingUid(); int userId = UserHandle.getCallingUserId(); long certSerial = 1000L; Loading Loading @@ -566,7 +566,31 @@ public class RecoverableKeyStoreManagerTest { TEST_SECRET))); fail("should have thrown"); } catch (ServiceSpecificException e) { assertThat(e.getMessage()).contains("CertPath is empty"); assertThat(e.getMessage()).contains("empty"); } } @Test public void startRecoverySessionWithCertPath_throwsIfInvalidCertPath() throws Exception { CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); CertPath shortCertPath = certFactory.generateCertPath( TestData.CERT_PATH_1.getCertificates() .subList(0, TestData.CERT_PATH_1.getCertificates().size() - 1)); try { mRecoverableKeyStoreManager.startRecoverySessionWithCertPath( TEST_SESSION_ID, RecoveryCertPath.createRecoveryCertPath(shortCertPath), TEST_VAULT_PARAMS, TEST_VAULT_CHALLENGE, ImmutableList.of( new KeyChainProtectionParams( TYPE_LOCKSCREEN, UI_FORMAT_PASSWORD, KeyDerivationParams.createSha256Params(TEST_SALT), TEST_SECRET))); fail("should have thrown"); } catch (ServiceSpecificException e) { // expected } } Loading services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/certificate/CertUtilsTest.java +38 −0 Original line number Diff line number Diff line Loading @@ -30,8 +30,10 @@ import java.io.InputStream; import java.security.KeyPairGenerator; import java.security.PublicKey; import java.security.cert.CertPath; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.security.spec.ECGenParameterSpec; import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; import java.util.Collections; Loading Loading @@ -316,6 +318,42 @@ public final class CertUtilsTest { leafCert))); } @Test public void validateCertPath_succeeds() throws Exception { X509Certificate rootCert = TestData.ROOT_CA_TRUSTED; List<X509Certificate> intermediateCerts = Arrays.asList(TestData.INTERMEDIATE_CA_1, TestData.INTERMEDIATE_CA_2); X509Certificate leafCert = TestData.LEAF_CERT_2; CertPath certPath = CertUtils.buildCertPath( CertUtils.buildPkixParams( TestData.DATE_ALL_CERTS_VALID, rootCert, intermediateCerts, leafCert)); CertUtils.validateCertPath( TestData.DATE_ALL_CERTS_VALID, TestData.ROOT_CA_TRUSTED, certPath); } @Test public void validateCertPath_throwsIfEmptyCertPath() throws Exception { CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); CertPath emptyCertPath = certFactory.generateCertPath(new ArrayList<X509Certificate>()); CertValidationException expected = expectThrows( CertValidationException.class, () -> CertUtils.validateCertPath(TestData.DATE_ALL_CERTS_VALID, TestData.ROOT_CA_TRUSTED, emptyCertPath)); assertThat(expected.getMessage()).contains("empty"); } @Test public void validateCertPath_throwsIfNotValidated() throws Exception { assertThrows( CertValidationException.class, () -> CertUtils.validateCertPath(TestData.DATE_ALL_CERTS_VALID, TestData.ROOT_CA_DIFFERENT_COMMON_NAME, com.android.server.locksettings.recoverablekeystore.TestData.CERT_PATH_1)); } @Test public void validateCert_succeeds() throws Exception { X509Certificate rootCert = TestData.ROOT_CA_TRUSTED; Loading Loading
services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java +8 −5 Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.HexDump; import com.android.server.locksettings.recoverablekeystore.certificate.CertUtils; import com.android.server.locksettings.recoverablekeystore.storage.ApplicationKeyStorage; import com.android.server.locksettings.recoverablekeystore.certificate.CertParsingException; import com.android.server.locksettings.recoverablekeystore.certificate.CertValidationException; Loading Loading @@ -446,12 +447,14 @@ public class RecoverableKeyStoreManager { "Failed decode the certificate path"); } // TODO: Validate the cert path according to the root of trust if (certPath.getCertificates().isEmpty()) { throw new ServiceSpecificException(ERROR_BAD_CERTIFICATE_FORMAT, "The given CertPath is empty"); try { CertUtils.validateCertPath(TrustedRootCert.TRUSTED_ROOT_CERT, 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()); } byte[] verifierPublicKey = certPath.getCertificates().get(0).getPublicKey().getEncoded(); if (verifierPublicKey == null) { Log.e(TAG, "Failed to encode verifierPublicKey"); Loading
services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertUtils.java +37 −0 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ import java.security.cert.CertPathBuilderException; import java.security.cert.CertPathValidator; import java.security.cert.CertPathValidatorException; import java.security.cert.CertStore; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.CollectionCertStoreParameters; Loading Loading @@ -292,6 +293,42 @@ public final class CertUtils { return certPath; } /** * Validates a given {@code CertPath} against the trusted root certificate. * * @param trustedRoot the trusted root certificate * @param certPath the certificate path to be validated * @throws CertValidationException if the given certificate path is invalid, e.g., is expired, * or does not have a valid signature */ public static void validateCertPath(X509Certificate trustedRoot, CertPath certPath) throws CertValidationException { validateCertPath(/*validationDate=*/ null, trustedRoot, certPath); } /** * Validates a given {@code CertPath} against a given {@code validationDate}. If the given * validation date is null, the current date will be used. */ @VisibleForTesting static void validateCertPath(@Nullable Date validationDate, X509Certificate trustedRoot, CertPath certPath) throws CertValidationException { if (certPath.getCertificates().isEmpty()) { throw new CertValidationException("The given certificate path is empty"); } if (!(certPath.getCertificates().get(0) instanceof X509Certificate)) { throw new CertValidationException( "The given certificate path does not contain X509 certificates"); } List<X509Certificate> certificates = (List<X509Certificate>) certPath.getCertificates(); X509Certificate leafCert = certificates.get(0); List<X509Certificate> intermediateCerts = certificates.subList(/*fromIndex=*/ 1, certificates.size()); validateCert(validationDate, trustedRoot, intermediateCerts, leafCert); } @VisibleForTesting static CertPath buildCertPath(PKIXParameters pkixParams) throws CertValidationException { CertPathBuilder certPathBuilder; Loading
services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java +26 −2 Original line number Diff line number Diff line Loading @@ -277,7 +277,7 @@ public class RecoverableKeyStoreManagerTest { } @Test public void initRecoveryService_succeeds() throws Exception { public void initRecoveryService_succeedsWithCertFile() throws Exception { int uid = Binder.getCallingUid(); int userId = UserHandle.getCallingUserId(); long certSerial = 1000L; Loading Loading @@ -566,7 +566,31 @@ public class RecoverableKeyStoreManagerTest { TEST_SECRET))); fail("should have thrown"); } catch (ServiceSpecificException e) { assertThat(e.getMessage()).contains("CertPath is empty"); assertThat(e.getMessage()).contains("empty"); } } @Test public void startRecoverySessionWithCertPath_throwsIfInvalidCertPath() throws Exception { CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); CertPath shortCertPath = certFactory.generateCertPath( TestData.CERT_PATH_1.getCertificates() .subList(0, TestData.CERT_PATH_1.getCertificates().size() - 1)); try { mRecoverableKeyStoreManager.startRecoverySessionWithCertPath( TEST_SESSION_ID, RecoveryCertPath.createRecoveryCertPath(shortCertPath), TEST_VAULT_PARAMS, TEST_VAULT_CHALLENGE, ImmutableList.of( new KeyChainProtectionParams( TYPE_LOCKSCREEN, UI_FORMAT_PASSWORD, KeyDerivationParams.createSha256Params(TEST_SALT), TEST_SECRET))); fail("should have thrown"); } catch (ServiceSpecificException e) { // expected } } Loading
services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/certificate/CertUtilsTest.java +38 −0 Original line number Diff line number Diff line Loading @@ -30,8 +30,10 @@ import java.io.InputStream; import java.security.KeyPairGenerator; import java.security.PublicKey; import java.security.cert.CertPath; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.security.spec.ECGenParameterSpec; import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; import java.util.Collections; Loading Loading @@ -316,6 +318,42 @@ public final class CertUtilsTest { leafCert))); } @Test public void validateCertPath_succeeds() throws Exception { X509Certificate rootCert = TestData.ROOT_CA_TRUSTED; List<X509Certificate> intermediateCerts = Arrays.asList(TestData.INTERMEDIATE_CA_1, TestData.INTERMEDIATE_CA_2); X509Certificate leafCert = TestData.LEAF_CERT_2; CertPath certPath = CertUtils.buildCertPath( CertUtils.buildPkixParams( TestData.DATE_ALL_CERTS_VALID, rootCert, intermediateCerts, leafCert)); CertUtils.validateCertPath( TestData.DATE_ALL_CERTS_VALID, TestData.ROOT_CA_TRUSTED, certPath); } @Test public void validateCertPath_throwsIfEmptyCertPath() throws Exception { CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); CertPath emptyCertPath = certFactory.generateCertPath(new ArrayList<X509Certificate>()); CertValidationException expected = expectThrows( CertValidationException.class, () -> CertUtils.validateCertPath(TestData.DATE_ALL_CERTS_VALID, TestData.ROOT_CA_TRUSTED, emptyCertPath)); assertThat(expected.getMessage()).contains("empty"); } @Test public void validateCertPath_throwsIfNotValidated() throws Exception { assertThrows( CertValidationException.class, () -> CertUtils.validateCertPath(TestData.DATE_ALL_CERTS_VALID, TestData.ROOT_CA_DIFFERENT_COMMON_NAME, com.android.server.locksettings.recoverablekeystore.TestData.CERT_PATH_1)); } @Test public void validateCert_succeeds() throws Exception { X509Certificate rootCert = TestData.ROOT_CA_TRUSTED; Loading