summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Bo Zhu <bozhu@google.com> 2018-02-28 15:14:49 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2018-02-28 15:14:49 +0000
commit3e58039f874e4a20ccc164e6351c04c83b3cf689 (patch)
tree2645b1141490b3e0586fb4751d3dd540c1c5e7a3
parent9371be3b88e2bcd6da6223b4cf59a00d32147133 (diff)
parent7ce4ea52b356c2c7e1e65f5d484b3b641d06343e (diff)
Merge "Check the given CertPath against the root of trust during recovery"
-rw-r--r--services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java13
-rw-r--r--services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertUtils.java37
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java28
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/certificate/CertUtilsTest.java38
4 files changed, 109 insertions, 7 deletions
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index da0b0d03b54d..1e0703a3994b 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -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;
@@ -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");
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertUtils.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertUtils.java
index 09ec5ad1d5dd..6e08949b634e 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertUtils.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertUtils.java
@@ -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;
@@ -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;
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index 199410c42b0e..e6a36c6776da 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
@@ -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;
@@ -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
}
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/certificate/CertUtilsTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/certificate/CertUtilsTest.java
index d08dab4752d5..9279698c6004 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/certificate/CertUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/certificate/CertUtilsTest.java
@@ -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;
@@ -317,6 +319,42 @@ public final class CertUtilsTest {
}
@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;
List<X509Certificate> intermediateCerts =