summaryrefslogtreecommitdiff
path: root/keystore/tests
diff options
context:
space:
mode:
authorEran Messeri <eranm@google.com>2017-11-15 05:55:52 +0000
committerEran Messeri <eranm@google.com>2017-12-07 15:12:30 +0000
commit852c8f121f2e502e1e8503bfc230dccb81b681d4 (patch)
tree27c90a754791b77990afbcb369cac3fad401a3bf /keystore/tests
parentd52efa56adaca0bc70fb72082c7c663adcb669cc (diff)
DevicePolicyManager: Add key generation functionality.
This is the crux of the Verified Access feature implementation: Adding the ability to generate KeyChain keys directly by the secure hardware, rather than installing software-generated keys into KeyChain. Add generateKeyPair to the DevicePolicyManager, which delegates key generation (via the DevicePolicyManagerService) to the KeyChainService. Design highlights: * The key generation is delegated via the DevicePolicyManagerService to check that only authorized callers request key generation in KeyChain. * KeyChainService performs the actual key generation so it owns the key in Keystore outright. * DevicePolicyManagerService then grants the calling app access to the Keystore key, so it can actually be used. * Loading the public/private key pair, as well as attestation certificate chain, is done in the client code (DevicePolicyManager) to save parceling / unparceling those objects across process boundaries twice (for no good reason). NOTE: The key attestation functionality (that includes Device ID) is missing/untested. Will be added in a follow-up CL as this one is quite big already. HIGHLIGHT FOR REVIEWERS: * API: New API in DevicePolicyManager. Bug: 63388672 Test: cts-tradefed run commandAndExit cts-dev -a armeabi-v7a -m CtsDevicePolicyManagerTestCases -t com.android.cts.devicepolicy.DeviceOwnerTest#testKeyManagement -l DEBUG; adb shell am instrument 'android.security.tests/android.support.test.runner.AndroidJUnitRunner' (After building the KeystoreTests target and installing the apk) Change-Id: I73762c9123f32a94d454ba4f8b533883b55c44cc
Diffstat (limited to 'keystore/tests')
-rw-r--r--keystore/tests/Android.mk34
-rw-r--r--keystore/tests/AndroidManifest.xml28
-rw-r--r--keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java180
3 files changed, 242 insertions, 0 deletions
diff --git a/keystore/tests/Android.mk b/keystore/tests/Android.mk
new file mode 100644
index 000000000000..51adde4d68af
--- /dev/null
+++ b/keystore/tests/Android.mk
@@ -0,0 +1,34 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+# LOCAL_MODULE := keystore
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test \
+ legacy-android-test
+
+LOCAL_PACKAGE_NAME := KeystoreTests
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
+
diff --git a/keystore/tests/AndroidManifest.xml b/keystore/tests/AndroidManifest.xml
new file mode 100644
index 000000000000..9bf2d0c761e6
--- /dev/null
+++ b/keystore/tests/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.security.tests">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.security.tests"
+ android:label="Tests for Keystore">
+ </instrumentation>
+</manifest>
+
diff --git a/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java b/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
new file mode 100644
index 000000000000..73b489f98e1d
--- /dev/null
+++ b/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import android.os.Parcel;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.ParcelableKeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
+import android.support.test.runner.AndroidJUnit4;
+import java.math.BigInteger;
+import java.security.spec.ECGenParameterSpec;
+import java.security.spec.RSAKeyGenParameterSpec;
+import java.util.Date;
+import javax.security.auth.x500.X500Principal;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Unit tests for {@link ParcelableKeyGenParameterSpec}. */
+@RunWith(AndroidJUnit4.class)
+public final class ParcelableKeyGenParameterSpecTest {
+ static final String ALIAS = "keystore-alias";
+ static final String ANOTHER_ALIAS = "another-keystore-alias";
+ static final int KEY_PURPOSES = KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY;
+ static final int UID = 1230;
+ static final int KEYSIZE = 2048;
+ static final X500Principal SUBJECT = new X500Principal("CN=subject");
+ static final BigInteger SERIAL = new BigInteger("1234567890");
+ static final Date NOT_BEFORE = new Date(1511799590);
+ static final Date NOT_AFTER = new Date(1511899590);
+ static final Date KEY_VALIDITY_START = new Date(1511799591);
+ static final Date KEY_VALIDITY_FOR_ORIG_END = new Date(1511799593);
+ static final Date KEY_VALIDITY_FOR_CONSUMPTION_END = new Date(1511799594);
+ static final String DIGEST = KeyProperties.DIGEST_SHA256;
+ static final String ENCRYPTION_PADDING = KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1;
+ static final String SIGNATURE_PADDING = KeyProperties.SIGNATURE_PADDING_RSA_PSS;
+ static final String BLOCK_MODE = KeyProperties.BLOCK_MODE_CBC;
+ static final int USER_AUTHENTICATION_DURATION = 300;
+ static final byte[] ATTESTATION_CHALLENGE = new byte[] {'c', 'h'};
+
+ KeyGenParameterSpec configureDefaultSpec() {
+ return new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES)
+ .setUid(UID)
+ .setKeySize(KEYSIZE)
+ .setCertificateSubject(SUBJECT)
+ .setCertificateSerialNumber(SERIAL)
+ .setCertificateNotBefore(NOT_BEFORE)
+ .setCertificateNotAfter(NOT_AFTER)
+ .setKeyValidityStart(KEY_VALIDITY_START)
+ .setKeyValidityForOriginationEnd(KEY_VALIDITY_FOR_ORIG_END)
+ .setKeyValidityForConsumptionEnd(KEY_VALIDITY_FOR_CONSUMPTION_END)
+ .setDigests(DIGEST)
+ .setEncryptionPaddings(ENCRYPTION_PADDING)
+ .setSignaturePaddings(SIGNATURE_PADDING)
+ .setBlockModes(BLOCK_MODE)
+ .setRandomizedEncryptionRequired(true)
+ .setUserAuthenticationRequired(true)
+ .setUserAuthenticationValidityDurationSeconds(USER_AUTHENTICATION_DURATION)
+ .setAttestationChallenge(ATTESTATION_CHALLENGE)
+ .setUniqueIdIncluded(true)
+ .setUserAuthenticationValidWhileOnBody(true)
+ .setInvalidatedByBiometricEnrollment(true)
+ .build();
+ }
+
+ void validateSpecValues(KeyGenParameterSpec spec, int uid, String alias) {
+ assertThat(spec.getKeystoreAlias(), is(alias));
+ assertThat(spec.getPurposes(), is(KEY_PURPOSES));
+ assertThat(spec.getUid(), is(uid));
+ assertThat(spec.getKeySize(), is(KEYSIZE));
+ assertThat(spec.getCertificateSubject(), is(SUBJECT));
+ assertThat(spec.getCertificateSerialNumber(), is(SERIAL));
+ assertThat(spec.getCertificateNotBefore(), is(NOT_BEFORE));
+ assertThat(spec.getCertificateNotAfter(), is(NOT_AFTER));
+ assertThat(spec.getKeyValidityStart(), is(KEY_VALIDITY_START));
+ assertThat(spec.getKeyValidityForOriginationEnd(), is(KEY_VALIDITY_FOR_ORIG_END));
+ assertThat(spec.getKeyValidityForConsumptionEnd(), is(KEY_VALIDITY_FOR_CONSUMPTION_END));
+ assertThat(spec.getDigests(), is(new String[] {DIGEST}));
+ assertThat(spec.getEncryptionPaddings(), is(new String[] {ENCRYPTION_PADDING}));
+ assertThat(spec.getSignaturePaddings(), is(new String[] {SIGNATURE_PADDING}));
+ assertThat(spec.getBlockModes(), is(new String[] {BLOCK_MODE}));
+ assertThat(spec.isRandomizedEncryptionRequired(), is(true));
+ assertThat(spec.isUserAuthenticationRequired(), is(true));
+ assertThat(
+ spec.getUserAuthenticationValidityDurationSeconds(),
+ is(USER_AUTHENTICATION_DURATION));
+ assertThat(spec.getAttestationChallenge(), is(ATTESTATION_CHALLENGE));
+ assertThat(spec.isUniqueIdIncluded(), is(true));
+ assertThat(spec.isUserAuthenticationValidWhileOnBody(), is(true));
+ assertThat(spec.isInvalidatedByBiometricEnrollment(), is(true));
+ }
+
+ private Parcel parcelForReading(ParcelableKeyGenParameterSpec spec) {
+ Parcel parcel = Parcel.obtain();
+ spec.writeToParcel(parcel, spec.describeContents());
+
+ parcel.setDataPosition(0);
+ return parcel;
+ }
+
+ @Test
+ public void testParcelingWithAllValues() {
+ ParcelableKeyGenParameterSpec spec =
+ new ParcelableKeyGenParameterSpec(configureDefaultSpec());
+ Parcel parcel = parcelForReading(spec);
+ ParcelableKeyGenParameterSpec fromParcel =
+ ParcelableKeyGenParameterSpec.CREATOR.createFromParcel(parcel);
+ validateSpecValues(fromParcel.getSpec(), UID, ALIAS);
+ assertThat(parcel.dataAvail(), is(0));
+ }
+
+ @Test
+ public void testParcelingWithNullValues() {
+ ParcelableKeyGenParameterSpec spec = new ParcelableKeyGenParameterSpec(
+ new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES).build());
+
+ Parcel parcel = parcelForReading(spec);
+ KeyGenParameterSpec fromParcel = ParcelableKeyGenParameterSpec.CREATOR
+ .createFromParcel(parcel)
+ .getSpec();
+ assertThat(fromParcel.getKeystoreAlias(), is(ALIAS));
+ assertThat(fromParcel.getPurposes(), is(KEY_PURPOSES));
+ assertThat(fromParcel.getCertificateNotBefore(), is(new Date(0L)));
+ assertThat(fromParcel.getCertificateNotAfter(), is(new Date(2461449600000L)));
+ assertThat(parcel.dataAvail(), is(0));
+ }
+
+ @Test
+ public void testParcelingRSAAlgoParameter() {
+ RSAKeyGenParameterSpec rsaSpec =
+ new RSAKeyGenParameterSpec(2048, new BigInteger("5231123"));
+ ParcelableKeyGenParameterSpec spec = new ParcelableKeyGenParameterSpec(
+ new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES)
+ .setAlgorithmParameterSpec(rsaSpec)
+ .build());
+
+ Parcel parcel = parcelForReading(spec);
+ KeyGenParameterSpec fromParcel =
+ ParcelableKeyGenParameterSpec.CREATOR.createFromParcel(parcel).getSpec();
+ RSAKeyGenParameterSpec parcelSpec =
+ (RSAKeyGenParameterSpec) fromParcel.getAlgorithmParameterSpec();
+ // Compare individual fields as RSAKeyGenParameterSpec, on android, does not
+ // implement equals()
+ assertEquals(parcelSpec.getKeysize(), rsaSpec.getKeysize());
+ assertEquals(parcelSpec.getPublicExponent(), rsaSpec.getPublicExponent());
+ }
+
+ @Test
+ public void testParcelingECAlgoParameter() {
+ ECGenParameterSpec ecSpec = new ECGenParameterSpec("P-256");
+ ParcelableKeyGenParameterSpec spec = new ParcelableKeyGenParameterSpec(
+ new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES)
+ .setAlgorithmParameterSpec(ecSpec)
+ .build());
+ Parcel parcel = parcelForReading(spec);
+ KeyGenParameterSpec fromParcel =
+ ParcelableKeyGenParameterSpec.CREATOR.createFromParcel(parcel).getSpec();
+ // Compare individual fields as ECGenParameterSpec, on android, does not
+ // implement equals()
+ ECGenParameterSpec parcelSpec = (ECGenParameterSpec) fromParcel.getAlgorithmParameterSpec();
+ assertEquals(parcelSpec.getName(), ecSpec.getName());
+ }
+}