| /* |
| * 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()); |
| } |
| } |
| } |