blob: 17307f7be2afeceffaa3aa45447cd5d8569c0522 [file] [log] [blame]
/*
* Copyright (C) 2015 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.keystore.cts;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyInfo;
import android.security.keystore.KeyProperties;
import android.test.MoreAsserts;
import junit.framework.TestCase;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Provider;
import java.security.Security;
import java.security.spec.InvalidKeySpecException;
import java.security.Provider.Service;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.SecretKeySpec;
public class SecretKeyFactoryTest extends TestCase {
private static final String EXPECTED_PROVIDER_NAME = TestUtils.EXPECTED_PROVIDER_NAME;
private static final String[] EXPECTED_ALGORITHMS = {
"AES",
"HmacSHA1",
"HmacSHA224",
"HmacSHA256",
"HmacSHA384",
"HmacSHA512",
};
public void testAlgorithmList() {
// Assert that Android Keystore Provider exposes exactly the expected SecretKeyFactory
// algorithms. We don't care whether the algorithms are exposed via aliases, as long as
// canonical names of algorithms are accepted. If the Provider exposes extraneous
// algorithms, it'll be caught because it'll have to expose at least one Service for such an
// algorithm, and this Service's algorithm will not be in the expected set.
Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
Set<Service> services = provider.getServices();
Set<String> actualAlgsLowerCase = new HashSet<String>();
Set<String> expectedAlgsLowerCase = new HashSet<String>(
Arrays.asList(TestUtils.toLowerCase(EXPECTED_ALGORITHMS)));
for (Service service : services) {
if ("SecretKeyFactory".equalsIgnoreCase(service.getType())) {
String algLowerCase = service.getAlgorithm().toLowerCase(Locale.US);
actualAlgsLowerCase.add(algLowerCase);
}
}
TestUtils.assertContentsInAnyOrder(actualAlgsLowerCase,
expectedAlgsLowerCase.toArray(new String[0]));
}
public void testGetKeySpecWithKeystoreKeyAndKeyInfoReflectsAllAuthorizations()
throws Exception {
Date keyValidityStart = new Date(System.currentTimeMillis() - TestUtils.DAY_IN_MILLIS);
Date keyValidityForOriginationEnd =
new Date(System.currentTimeMillis() + TestUtils.DAY_IN_MILLIS);
Date keyValidityForConsumptionEnd =
new Date(System.currentTimeMillis() + 3 * TestUtils.DAY_IN_MILLIS);
for (String algorithm : EXPECTED_ALGORITHMS) {
try {
String[] blockModes =
new String[] {KeyProperties.BLOCK_MODE_CTR, KeyProperties.BLOCK_MODE_ECB};
String[] encryptionPaddings =
new String[] {KeyProperties.ENCRYPTION_PADDING_PKCS7,
KeyProperties.ENCRYPTION_PADDING_NONE};
String[] digests;
int purposes;
if (TestUtils.isHmacAlgorithm(algorithm)) {
String digest = TestUtils.getHmacAlgorithmDigest(algorithm);
digests = new String[] {digest};
purposes = KeyProperties.PURPOSE_SIGN;
} else {
digests = new String[] {KeyProperties.DIGEST_SHA384};
purposes = KeyProperties.PURPOSE_DECRYPT;
}
KeyGenerator keyGenerator =
KeyGenerator.getInstance(algorithm, EXPECTED_PROVIDER_NAME);
keyGenerator.init(new KeyGenParameterSpec.Builder("test1", purposes)
.setBlockModes(blockModes)
.setEncryptionPaddings(encryptionPaddings)
.setDigests(digests)
.setKeyValidityStart(keyValidityStart)
.setKeyValidityForOriginationEnd(keyValidityForOriginationEnd)
.setKeyValidityForConsumptionEnd(keyValidityForConsumptionEnd)
.build());
SecretKey key = keyGenerator.generateKey();
SecretKeyFactory keyFactory = getKeyFactory(algorithm);
KeyInfo keyInfo = (KeyInfo) keyFactory.getKeySpec(key, KeyInfo.class);
assertEquals("test1", keyInfo.getKeystoreAlias());
assertEquals(purposes, keyInfo.getPurposes());
TestUtils.assertContentsInAnyOrder(
Arrays.asList(blockModes), keyInfo.getBlockModes());
TestUtils.assertContentsInAnyOrder(
Arrays.asList(encryptionPaddings), keyInfo.getEncryptionPaddings());
TestUtils.assertContentsInAnyOrder(Arrays.asList(digests), keyInfo.getDigests());
MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getSignaturePaddings()));
assertEquals(keyValidityStart, keyInfo.getKeyValidityStart());
assertEquals(keyValidityForOriginationEnd,
keyInfo.getKeyValidityForOriginationEnd());
assertEquals(keyValidityForConsumptionEnd,
keyInfo.getKeyValidityForConsumptionEnd());
assertFalse(keyInfo.isUserAuthenticationRequired());
assertFalse(keyInfo.isUserAuthenticationRequirementEnforcedBySecureHardware());
} catch (Throwable e) {
throw new RuntimeException("Failed for " + algorithm, e);
}
}
}
public void testTranslateKeyWithNullKeyThrowsInvalidKeyException() throws Exception {
for (String algorithm : EXPECTED_ALGORITHMS) {
try {
SecretKeyFactory keyFactory = getKeyFactory(algorithm);
try {
keyFactory.translateKey(null);
fail();
} catch (InvalidKeyException expected) {}
} catch (Throwable e) {
throw new RuntimeException("Failed for " + algorithm, e);
}
}
}
public void testTranslateKeyRejectsNonAndroidKeystoreKeys() throws Exception {
for (String algorithm : EXPECTED_ALGORITHMS) {
try {
SecretKey key = new SecretKeySpec(new byte[16], algorithm);
SecretKeyFactory keyFactory = getKeyFactory(algorithm);
try {
keyFactory.translateKey(key);
fail();
} catch (InvalidKeyException expected) {}
} catch (Throwable e) {
throw new RuntimeException("Failed for " + algorithm, e);
}
}
}
public void testTranslateKeyAcceptsAndroidKeystoreKeys() throws Exception {
for (String algorithm : EXPECTED_ALGORITHMS) {
try {
KeyGenerator keyGenerator =
KeyGenerator.getInstance(algorithm, EXPECTED_PROVIDER_NAME);
keyGenerator.init(new KeyGenParameterSpec.Builder("test1", 0).build());
SecretKey key = keyGenerator.generateKey();
SecretKeyFactory keyFactory = getKeyFactory(algorithm);
assertSame(key, keyFactory.translateKey(key));
} catch (Throwable e) {
throw new RuntimeException("Failed for " + algorithm, e);
}
}
}
public void testGenerateSecretWithNullSpecThrowsInvalidKeySpecException() throws Exception {
for (String algorithm : EXPECTED_ALGORITHMS) {
try {
SecretKeyFactory keyFactory = getKeyFactory(algorithm);
try {
keyFactory.generateSecret(null);
fail();
} catch (InvalidKeySpecException expected) {}
} catch (Throwable e) {
throw new RuntimeException("Failed for " + algorithm, e);
}
}
}
public void testGenerateSecretRejectsSecretKeySpec() throws Exception {
for (String algorithm : EXPECTED_ALGORITHMS) {
try {
SecretKeyFactory keyFactory = getKeyFactory(algorithm);
try {
keyFactory.generateSecret(new SecretKeySpec(new byte[16], algorithm));
fail();
} catch (InvalidKeySpecException expected) {}
} catch (Throwable e) {
throw new RuntimeException("Failed for " + algorithm, e);
}
}
}
public void testGenerateSecretRejectsKeyInfo() throws Exception {
for (String algorithm : EXPECTED_ALGORITHMS) {
try {
KeyGenerator keyGenerator =
KeyGenerator.getInstance(algorithm, EXPECTED_PROVIDER_NAME);
keyGenerator.init(new KeyGenParameterSpec.Builder("test1", 0).build());
SecretKey keystoreKey = keyGenerator.generateKey();
KeyInfo keyInfo = TestUtils.getKeyInfo(keystoreKey);
SecretKeyFactory keyFactory = getKeyFactory(algorithm);
try {
keyFactory.generateSecret(keyInfo);
fail();
} catch (InvalidKeySpecException expected) {}
} catch (Throwable e) {
throw new RuntimeException("Failed for " + algorithm, e);
}
}
}
private SecretKeyFactory getKeyFactory(String algorithm) throws NoSuchAlgorithmException,
NoSuchProviderException {
return SecretKeyFactory.getInstance(algorithm, EXPECTED_PROVIDER_NAME);
}
}