diff options
author | Eran Messeri <eranm@google.com> | 2017-11-15 05:55:52 +0000 |
---|---|---|
committer | Eran Messeri <eranm@google.com> | 2017-12-07 15:12:30 +0000 |
commit | 852c8f121f2e502e1e8503bfc230dccb81b681d4 (patch) | |
tree | 27c90a754791b77990afbcb369cac3fad401a3bf /keystore/tests | |
parent | d52efa56adaca0bc70fb72082c7c663adcb669cc (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.mk | 34 | ||||
-rw-r--r-- | keystore/tests/AndroidManifest.xml | 28 | ||||
-rw-r--r-- | keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java | 180 |
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()); + } +} |