blob: 9f6843a4b509360e87a453eb910391f0e3d893a2 [file] [log] [blame]
/*
* Copyright (C) 2011 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 libcore.javax.crypto;
import java.security.AlgorithmParameters;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.Provider;
import java.security.Security;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import junit.framework.TestCase;
import libcore.java.security.StandardNames;
public final class CipherTest extends TestCase {
private static abstract class MockProvider extends Provider {
public MockProvider(String name) {
super(name, 1.0, "Mock provider used for testing");
setup();
}
public abstract void setup();
}
public void testCipher_getInstance_SuppliedProviderNotRegistered_Success() throws Exception {
Provider mockProvider = new MockProvider("MockProvider") {
public void setup() {
put("Cipher.FOO", MockCipherSpi.AllKeyTypes.class.getName());
}
};
{
Cipher c = Cipher.getInstance("FOO", mockProvider);
c.init(Cipher.ENCRYPT_MODE, new MockKey());
assertEquals(mockProvider, c.getProvider());
}
}
public void testCipher_getInstance_DoesNotSupportKeyClass_Success() throws Exception {
Provider mockProvider = new MockProvider("MockProvider") {
public void setup() {
put("Cipher.FOO", MockCipherSpi.AllKeyTypes.class.getName());
put("Cipher.FOO SupportedKeyClasses", "None");
}
};
Security.addProvider(mockProvider);
try {
Cipher c = Cipher.getInstance("FOO", mockProvider);
c.init(Cipher.ENCRYPT_MODE, new MockKey());
assertEquals(mockProvider, c.getProvider());
} finally {
Security.removeProvider(mockProvider.getName());
}
}
public void testCipher_getInstance_SuppliedProviderNotRegistered_MultipartTransform_Success()
throws Exception {
Provider mockProvider = new MockProvider("MockProvider") {
public void setup() {
put("Cipher.FOO", MockCipherSpi.AllKeyTypes.class.getName());
}
};
{
Cipher c = Cipher.getInstance("FOO/FOO/FOO", mockProvider);
c.init(Cipher.ENCRYPT_MODE, new MockKey());
assertEquals(mockProvider, c.getProvider());
}
}
public void testCipher_getInstance_OnlyUsesSpecifiedProvider_SameNameAndClass_Success()
throws Exception {
Provider mockProvider = new MockProvider("MockProvider") {
public void setup() {
put("Cipher.FOO", MockCipherSpi.AllKeyTypes.class.getName());
}
};
Security.addProvider(mockProvider);
try {
{
Provider mockProvider2 = new MockProvider("MockProvider") {
public void setup() {
put("Cipher.FOO", MockCipherSpi.AllKeyTypes.class.getName());
}
};
Cipher c = Cipher.getInstance("FOO", mockProvider2);
assertEquals(mockProvider2, c.getProvider());
}
} finally {
Security.removeProvider(mockProvider.getName());
}
}
public void testCipher_getInstance_DelayedInitialization_KeyType() throws Exception {
Provider mockProviderSpecific = new MockProvider("MockProviderSpecific") {
public void setup() {
put("Cipher.FOO", MockCipherSpi.SpecificKeyTypes.class.getName());
put("Cipher.FOO SupportedKeyClasses", MockKey.class.getName());
}
};
Provider mockProviderSpecific2 = new MockProvider("MockProviderSpecific2") {
public void setup() {
put("Cipher.FOO", MockCipherSpi.SpecificKeyTypes2.class.getName());
put("Cipher.FOO SupportedKeyClasses", MockKey2.class.getName());
}
};
Provider mockProviderAll = new MockProvider("MockProviderAll") {
public void setup() {
put("Cipher.FOO", MockCipherSpi.AllKeyTypes.class.getName());
}
};
Security.addProvider(mockProviderSpecific);
Security.addProvider(mockProviderSpecific2);
Security.addProvider(mockProviderAll);
try {
{
System.out.println(Arrays.deepToString(Security.getProviders("Cipher.FOO")));
Cipher c = Cipher.getInstance("FOO");
c.init(Cipher.ENCRYPT_MODE, new MockKey());
assertEquals(mockProviderSpecific, c.getProvider());
try {
c.init(Cipher.ENCRYPT_MODE, new MockKey2());
assertEquals(mockProviderSpecific2, c.getProvider());
if (StandardNames.IS_RI) {
fail("RI was broken before; fix tests now that it works!");
}
} catch (InvalidKeyException e) {
if (!StandardNames.IS_RI) {
fail("Non-RI should select the right provider");
}
}
}
{
Cipher c = Cipher.getInstance("FOO");
c.init(Cipher.ENCRYPT_MODE, new Key() {
@Override
public String getAlgorithm() {
throw new UnsupportedOperationException("not implemented");
}
@Override
public String getFormat() {
throw new UnsupportedOperationException("not implemented");
}
@Override
public byte[] getEncoded() {
throw new UnsupportedOperationException("not implemented");
}
});
assertEquals(mockProviderAll, c.getProvider());
}
{
Cipher c = Cipher.getInstance("FOO");
assertEquals(mockProviderSpecific, c.getProvider());
}
} finally {
Security.removeProvider(mockProviderSpecific.getName());
Security.removeProvider(mockProviderSpecific2.getName());
Security.removeProvider(mockProviderAll.getName());
}
}
public void testCipher_getInstance_CorrectPriority_AlgorithmOnlyFirst() throws Exception {
Provider mockProviderOnlyAlgorithm = new MockProvider("MockProviderOnlyAlgorithm") {
public void setup() {
put("Cipher.FOO", MockCipherSpi.AllKeyTypes.class.getName());
}
};
Provider mockProviderFullTransformSpecified = new MockProvider("MockProviderFull") {
public void setup() {
put("Cipher.FOO/FOO/FOO", MockCipherSpi.AllKeyTypes.class.getName());
}
};
Security.addProvider(mockProviderOnlyAlgorithm);
Security.addProvider(mockProviderFullTransformSpecified);
try {
Cipher c = Cipher.getInstance("FOO/FOO/FOO");
assertEquals(mockProviderOnlyAlgorithm, c.getProvider());
} finally {
Security.removeProvider(mockProviderOnlyAlgorithm.getName());
Security.removeProvider(mockProviderFullTransformSpecified.getName());
}
}
public void testCipher_getInstance_CorrectPriority_FullTransformFirst() throws Exception {
Provider mockProviderOnlyAlgorithm = new MockProvider("MockProviderOnlyAlgorithm") {
public void setup() {
put("Cipher.FOO", MockCipherSpi.AllKeyTypes.class.getName());
}
};
Provider mockProviderFullTransformSpecified = new MockProvider("MockProviderFull") {
public void setup() {
put("Cipher.FOO/FOO/FOO", MockCipherSpi.AllKeyTypes.class.getName());
}
};
Security.addProvider(mockProviderFullTransformSpecified);
Security.addProvider(mockProviderOnlyAlgorithm);
try {
Cipher c = Cipher.getInstance("FOO/FOO/FOO");
assertEquals(mockProviderFullTransformSpecified, c.getProvider());
} finally {
Security.removeProvider(mockProviderOnlyAlgorithm.getName());
Security.removeProvider(mockProviderFullTransformSpecified.getName());
}
}
public void testCipher_getInstance_CorrectPriority_AliasedAlgorithmFirst() throws Exception {
Provider mockProviderAliasedAlgorithm = new MockProvider("MockProviderAliasedAlgorithm") {
public void setup() {
put("Cipher.BAR", MockCipherSpi.AllKeyTypes.class.getName());
put("Alg.Alias.Cipher.FOO", "BAR");
}
};
Provider mockProviderAlgorithmOnly = new MockProvider("MockProviderAlgorithmOnly") {
public void setup() {
put("Cipher.FOO", MockCipherSpi.AllKeyTypes.class.getName());
}
};
Security.addProvider(mockProviderAliasedAlgorithm);
Security.addProvider(mockProviderAlgorithmOnly);
try {
Cipher c = Cipher.getInstance("FOO/FOO/FOO");
assertEquals(mockProviderAliasedAlgorithm, c.getProvider());
} finally {
Security.removeProvider(mockProviderAliasedAlgorithm.getName());
Security.removeProvider(mockProviderAlgorithmOnly.getName());
}
}
public void testCipher_getInstance_WrongType_Failure() throws Exception {
Provider mockProviderInvalid = new MockProvider("MockProviderInvalid") {
public void setup() {
put("Cipher.FOO", Object.class.getName());
}
};
Security.addProvider(mockProviderInvalid);
try {
Cipher c = Cipher.getInstance("FOO");
c.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(new byte[16], "FOO"));
fail("Should not find any matching providers; found: " + c);
} catch (ClassCastException expected) {
} finally {
Security.removeProvider(mockProviderInvalid.getName());
}
}
public void testCipher_init_CallsInitWithParams_AlgorithmParameterSpec() throws Exception {
Provider mockProviderRejects = new MockProvider("MockProviderRejects") {
public void setup() {
put("Cipher.FOO",
MockCipherSpi.MustInitWithAlgorithmParameterSpec_RejectsAll.class.getName());
put("Cipher.FOO SupportedKeyClasses", MockKey.class.getName());
}
};
Provider mockProviderAccepts = new MockProvider("MockProviderAccepts") {
public void setup() {
put("Cipher.FOO", MockCipherSpi.AllKeyTypes.class.getName());
put("Cipher.FOO SupportedKeyClasses", MockKey.class.getName());
}
};
Security.addProvider(mockProviderRejects);
Security.addProvider(mockProviderAccepts);
try {
Cipher c = Cipher.getInstance("FOO");
c.init(Cipher.ENCRYPT_MODE, new MockKey(), new IvParameterSpec(new byte[12]));
assertEquals(mockProviderAccepts, c.getProvider());
} finally {
Security.removeProvider(mockProviderRejects.getName());
Security.removeProvider(mockProviderAccepts.getName());
}
}
public void testCipher_init_CallsInitWithParams_AlgorithmParameters() throws Exception {
Provider mockProviderRejects = new MockProvider("MockProviderRejects") {
public void setup() {
put("Cipher.FOO",
MockCipherSpi.MustInitWithAlgorithmParameters_RejectsAll.class.getName());
put("Cipher.FOO SupportedKeyClasses", MockKey.class.getName());
}
};
Provider mockProviderAccepts = new MockProvider("MockProviderAccepts") {
public void setup() {
put("Cipher.FOO", MockCipherSpi.AllKeyTypes.class.getName());
put("Cipher.FOO SupportedKeyClasses", MockKey.class.getName());
}
};
Security.addProvider(mockProviderRejects);
Security.addProvider(mockProviderAccepts);
try {
Cipher c = Cipher.getInstance("FOO");
c.init(Cipher.ENCRYPT_MODE, new MockKey(), AlgorithmParameters.getInstance("AES"));
assertEquals(mockProviderAccepts, c.getProvider());
} finally {
Security.removeProvider(mockProviderRejects.getName());
Security.removeProvider(mockProviderAccepts.getName());
}
}
public void testCipher_init_CallsInitIgnoresRuntimeException() throws Exception {
Provider mockProviderRejects = new MockProvider("MockProviderRejects") {
public void setup() {
put("Cipher.FOO",
MockCipherSpi.MustInitWithAlgorithmParameters_ThrowsNull.class.getName());
put("Cipher.FOO SupportedKeyClasses", MockKey.class.getName());
}
};
Provider mockProviderAccepts = new MockProvider("MockProviderAccepts") {
public void setup() {
put("Cipher.FOO", MockCipherSpi.AllKeyTypes.class.getName());
put("Cipher.FOO SupportedKeyClasses", MockKey.class.getName());
}
};
Security.addProvider(mockProviderRejects);
Security.addProvider(mockProviderAccepts);
try {
Cipher c = Cipher.getInstance("FOO");
c.init(Cipher.ENCRYPT_MODE, new MockKey(), AlgorithmParameters.getInstance("AES"));
assertEquals(mockProviderAccepts, c.getProvider());
} finally {
Security.removeProvider(mockProviderRejects.getName());
Security.removeProvider(mockProviderAccepts.getName());
}
}
public void testCipher_init_CallsInitWithMode() throws Exception {
Provider mockProviderOnlyEncrypt = new MockProvider("MockProviderOnlyEncrypt") {
public void setup() {
put("Cipher.FOO", MockCipherSpi.MustInitForEncryptModeOrRejects.class.getName());
put("Cipher.FOO SupportedKeyClasses", MockKey.class.getName());
}
};
Provider mockProviderAcceptsAll = new MockProvider("MockProviderAcceptsAll") {
public void setup() {
put("Cipher.FOO", MockCipherSpi.AllKeyTypes.class.getName());
put("Cipher.FOO SupportedKeyClasses", MockKey.class.getName());
}
};
Security.addProvider(mockProviderOnlyEncrypt);
Security.addProvider(mockProviderAcceptsAll);
try {
{
Cipher c = Cipher.getInstance("FOO");
c.init(Cipher.DECRYPT_MODE, new MockKey(), AlgorithmParameters.getInstance("AES"));
assertEquals(mockProviderAcceptsAll, c.getProvider());
}
{
Cipher c = Cipher.getInstance("FOO");
c.init(Cipher.ENCRYPT_MODE, new MockKey(), AlgorithmParameters.getInstance("AES"));
assertEquals(mockProviderOnlyEncrypt, c.getProvider());
}
} finally {
Security.removeProvider(mockProviderOnlyEncrypt.getName());
Security.removeProvider(mockProviderAcceptsAll.getName());
}
}
/**
* Several exceptions can be thrown by init. Check that in this case we throw the right one,
* as the error could fall under the umbrella of other exceptions.
* http://b/18987633
*/
public void testCipher_init_DoesNotSupportKeyClass_throwsInvalidKeyException()
throws Exception {
Provider mockProvider = new MockProvider("MockProvider") {
public void setup() {
put("Cipher.FOO", MockCipherSpi.AllKeyTypes.class.getName());
put("Cipher.FOO SupportedKeyClasses", "none");
}
};
Security.addProvider(mockProvider);
try {
Cipher c = Cipher.getInstance("FOO");
c.init(Cipher.DECRYPT_MODE, new MockKey());
fail("Expected InvalidKeyException");
} catch (InvalidKeyException expected) {
} finally {
Security.removeProvider(mockProvider.getName());
}
}
/**
* If a provider rejects a key for "Cipher/Mode/Padding"", there might be another that
* accepts the key for "Cipher". Don't throw InvalidKeyException when trying the first one.
* http://b/22208820
*/
public void testCipher_init_tryAllCombinationsBeforeThrowingInvalidKey()
throws Exception {
Provider mockProvider = new MockProvider("MockProvider") {
public void setup() {
put("Cipher.FOO/FOO/FOO", MockCipherSpi.AllKeyTypes.class.getName());
put("Cipher.FOO/FOO/FOO SupportedKeyClasses", "none");
}
};
Provider mockProvider2 = new MockProvider("MockProvider2") {
public void setup() {
put("Cipher.FOO", MockCipherSpi.AllKeyTypes.class.getName());
}
};
Security.addProvider(mockProvider);
try {
try {
// The provider installed doesn't accept the key.
Cipher c = Cipher.getInstance("FOO/FOO/FOO");
c.init(Cipher.DECRYPT_MODE, new MockKey());
fail("Expected InvalidKeyException");
} catch (InvalidKeyException expected) {
}
Security.addProvider(mockProvider2);
try {
// The new provider accepts "FOO" with this key. Use it despite the other provider
// accepts "FOO/FOO/FOO" but doesn't accept the key.
Cipher c = Cipher.getInstance("FOO/FOO/FOO");
c.init(Cipher.DECRYPT_MODE, new MockKey());
assertEquals("MockProvider2", c.getProvider().getName());
} finally {
Security.removeProvider(mockProvider2.getName());
}
} finally {
Security.removeProvider(mockProvider.getName());
}
}
/**
* http://b/29038928
* If in a second call to init the current spi doesn't support the new specified key, look for
* another suitable spi.
*/
public void test_init_onKeyTypeChange_reInitCipher() throws Exception {
Provider mockProvider = new MockProvider("MockProvider") {
public void setup() {
put("Cipher.FOO", MockCipherSpi.SpecificKeyTypes.class.getName());
}
};
Provider mockProvider2 = new MockProvider("MockProvider2") {
public void setup() {
put("Cipher.FOO", MockCipherSpi.SpecificKeyTypes2.class.getName());
}
};
try {
Security.addProvider(mockProvider);
Security.addProvider(mockProvider2);
Cipher cipher = Cipher.getInstance("FOO");
cipher.init(Cipher.ENCRYPT_MODE, new MockKey());
assertEquals("MockProvider", cipher.getProvider().getName());
// Using a different key...
cipher.init(Cipher.ENCRYPT_MODE, new MockKey2());
// ...results in a different provider.
assertEquals("MockProvider2", cipher.getProvider().getName());
} finally {
Security.removeProvider(mockProvider.getName());
Security.removeProvider(mockProvider2.getName());
}
}
/**
* http://b/29038928
* If in a second call to init the current spi doesn't support the new specified
* {@link AlgorithmParameterSpec}, look for another suitable spi.
*/
public void test_init_onAlgorithmParameterTypeChange_reInitCipher() throws Exception {
Provider mockProvider = new MockProvider("MockProvider") {
public void setup() {
put("Cipher.FOO",
MockCipherSpi.SpecificAlgorithmParameterSpecTypes.class.getName());
}
};
Provider mockProvider2 = new MockProvider("MockProvider2") {
public void setup() {
put("Cipher.FOO",
MockCipherSpi.SpecificAlgorithmParameterSpecTypes2.class.getName());
}
};
try {
Security.addProvider(mockProvider);
Security.addProvider(mockProvider2);
Cipher cipher = Cipher.getInstance("FOO");
cipher.init(Cipher.ENCRYPT_MODE,
new MockKey(),
new MockCipherSpi.MockAlgorithmParameterSpec());
assertEquals("MockProvider", cipher.getProvider().getName());
// Using a different AlgorithmParameterSpec...
cipher.init(Cipher.ENCRYPT_MODE,
new MockKey(),
new MockCipherSpi.MockAlgorithmParameterSpec2());
// ...results in a different provider.
assertEquals("MockProvider2", cipher.getProvider().getName());
} finally {
Security.removeProvider(mockProvider.getName());
Security.removeProvider(mockProvider2.getName());
}
}
/**
* http://b/29038928
* If in a second call to init the current spi doesn't support the new specified
* {@link AlgorithmParameters}, look for another suitable spi.
*/
public void test_init_onAlgorithmParametersChange_reInitCipher() throws Exception {
Provider mockProvider = new MockProvider("MockProvider") {
public void setup() {
put("Cipher.FOO",
MockCipherSpi.SpecificAlgorithmParameterAesAlgorithm.class.getName());
}
};
Provider mockProvider2 = new MockProvider("MockProvider2") {
public void setup() {
put("Cipher.FOO",
MockCipherSpi.SpecificAlgorithmParametersDesAlgorithm.class.getName());
}
};
try {
Security.addProvider(mockProvider);
Security.addProvider(mockProvider2);
Cipher cipher = Cipher.getInstance("FOO");
cipher.init(Cipher.ENCRYPT_MODE,
new MockKey(),
AlgorithmParameters.getInstance("AES"));
assertEquals("MockProvider", cipher.getProvider().getName());
// Using a different AlgorithmParameters...
cipher.init(Cipher.ENCRYPT_MODE,
new MockKey(),
AlgorithmParameters.getInstance("DES"));
// ...results in a different provider.
assertEquals("MockProvider2", cipher.getProvider().getName());
} finally {
Security.removeProvider(mockProvider.getName());
Security.removeProvider(mockProvider2.getName());
}
}
}