diff options
author | Kenny Root <kroot@google.com> | 2014-02-07 16:45:28 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2014-02-07 16:45:28 +0000 |
commit | 6e73b94717973b36085f61233195bcf001687581 (patch) | |
tree | 1fa919f9a8f3275535e54b8921cbaf8d6467d1d7 /luni | |
parent | be8f188d3da3feea104f67209d0e0434272d8d18 (diff) | |
parent | 91e77f683cc56948748185ff821a9c2ee11540ba (diff) | |
download | libcore-6e73b94717973b36085f61233195bcf001687581.zip libcore-6e73b94717973b36085f61233195bcf001687581.tar.gz libcore-6e73b94717973b36085f61233195bcf001687581.tar.bz2 |
Merge "Late binding: add support to Mac"
Diffstat (limited to 'luni')
3 files changed, 343 insertions, 35 deletions
diff --git a/luni/src/main/java/javax/crypto/Mac.java b/luni/src/main/java/javax/crypto/Mac.java index d74c2b9..5a73dc5 100644 --- a/luni/src/main/java/javax/crypto/Mac.java +++ b/luni/src/main/java/javax/crypto/Mac.java @@ -24,8 +24,10 @@ import java.security.Key; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.Provider; +import java.security.ProviderException; import java.security.Security; import java.security.spec.AlgorithmParameterSpec; +import java.util.ArrayList; import org.apache.harmony.security.fortress.Engine; @@ -35,18 +37,29 @@ import org.apache.harmony.security.fortress.Engine; */ public class Mac implements Cloneable { + // The service name. + private static final String SERVICE = "Mac"; + //Used to access common engine functionality - private static final Engine ENGINE = new Engine("Mac"); + private static final Engine ENGINE = new Engine(SERVICE); // Store used provider - private final Provider provider; + private Provider provider; + + // Provider that was requested during creation. + private final Provider specifiedProvider; // Store used spi implementation - private final MacSpi spiImpl; + private MacSpi spiImpl; // Store used algorithm name private final String algorithm; + /** + * Lock held while the SPI is initializing. + */ + private final Object initLock = new Object(); + // Store Mac state (initialized or not initialized) private boolean isInitMac; @@ -61,7 +74,7 @@ public class Mac implements Cloneable { * the name of the MAC algorithm. */ protected Mac(MacSpi macSpi, Provider provider, String algorithm) { - this.provider = provider; + this.specifiedProvider = provider; this.algorithm = algorithm; this.spiImpl = macSpi; this.isInitMac = false; @@ -82,6 +95,7 @@ public class Mac implements Cloneable { * @return the provider of this {@code Mac} instance. */ public final Provider getProvider() { + getSpi(); return provider; } @@ -100,11 +114,7 @@ public class Mac implements Cloneable { */ public static final Mac getInstance(String algorithm) throws NoSuchAlgorithmException { - if (algorithm == null) { - throw new NullPointerException("algorithm == null"); - } - Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null); - return new Mac((MacSpi) sap.spi, sap.provider, algorithm); + return getMac(algorithm, null); } /** @@ -136,7 +146,7 @@ public class Mac implements Cloneable { if (impProvider == null) { throw new NoSuchProviderException(provider); } - return getInstance(algorithm, impProvider); + return getMac(algorithm, impProvider); } /** @@ -163,11 +173,102 @@ public class Mac implements Cloneable { if (provider == null) { throw new IllegalArgumentException("provider == null"); } + return getMac(algorithm, provider); + } + + private static Mac getMac(String algorithm, Provider provider) + throws NoSuchAlgorithmException { if (algorithm == null) { throw new NullPointerException("algorithm == null"); } - Object spi = ENGINE.getInstance(algorithm, provider, null); - return new Mac((MacSpi) spi, provider, algorithm); + + if (tryAlgorithm(null, provider, algorithm) == null) { + if (provider == null) { + throw new NoSuchAlgorithmException("No provider found for " + algorithm); + } else { + throw new NoSuchAlgorithmException("Provider " + provider.getName() + + " does not provide " + algorithm); + } + } + return new Mac(null, provider, algorithm); + } + + private static Engine.SpiAndProvider tryAlgorithm(Key key, Provider provider, String algorithm) { + if (provider != null) { + Provider.Service service = provider.getService(SERVICE, algorithm); + if (service == null) { + return null; + } + return tryAlgorithmWithProvider(key, service); + } + ArrayList<Provider.Service> services = ENGINE.getServices(algorithm); + if (services == null) { + return null; + } + for (Provider.Service service : services) { + Engine.SpiAndProvider sap = tryAlgorithmWithProvider(key, service); + if (sap != null) { + return sap; + } + } + return null; + } + + private static Engine.SpiAndProvider tryAlgorithmWithProvider(Key key, Provider.Service service) { + try { + if (key != null && !service.supportsParameter(key)) { + return null; + } + + Engine.SpiAndProvider sap = ENGINE.getInstance(service, null); + if (sap.spi == null || sap.provider == null) { + return null; + } + if (!(sap.spi instanceof MacSpi)) { + return null; + } + return sap; + } catch (NoSuchAlgorithmException ignored) { + } + return null; + } + + /** + * Makes sure a MacSpi that matches this type is selected. + */ + private MacSpi getSpi(Key key) { + synchronized (initLock) { + if (spiImpl != null && provider != null && key == null) { + return spiImpl; + } + + if (algorithm == null) { + return null; + } + + final Engine.SpiAndProvider sap = tryAlgorithm(key, specifiedProvider, algorithm); + if (sap == null) { + throw new ProviderException("No provider for " + getAlgorithm()); + } + + /* + * Set our Spi if we've never been initialized or if we have the Spi + * specified and have a null provider. + */ + if (spiImpl == null || provider != null) { + spiImpl = (MacSpi) sap.spi; + } + provider = sap.provider; + + return spiImpl; + } + } + + /** + * Convenience call when the Key is not available. + */ + private MacSpi getSpi() { + return getSpi(null); } /** @@ -176,7 +277,7 @@ public class Mac implements Cloneable { * @return the length of this MAC (in bytes). */ public final int getMacLength() { - return spiImpl.engineGetMacLength(); + return getSpi().engineGetMacLength(); } /** @@ -199,7 +300,7 @@ public class Mac implements Cloneable { if (key == null) { throw new InvalidKeyException("key == null"); } - spiImpl.engineInit(key, params); + getSpi(key).engineInit(key, params); isInitMac = true; } @@ -220,7 +321,7 @@ public class Mac implements Cloneable { throw new InvalidKeyException("key == null"); } try { - spiImpl.engineInit(key, null); + getSpi(key).engineInit(key, null); isInitMac = true; } catch (InvalidAlgorithmParameterException e) { throw new RuntimeException(e); @@ -239,7 +340,7 @@ public class Mac implements Cloneable { if (!isInitMac) { throw new IllegalStateException(); } - spiImpl.engineUpdate(input); + getSpi().engineUpdate(input); } /** @@ -270,7 +371,7 @@ public class Mac implements Cloneable { + " input.length=" + input.length + " offset=" + offset + ", len=" + len); } - spiImpl.engineUpdate(input, offset, len); + getSpi().engineUpdate(input, offset, len); } /** @@ -286,7 +387,7 @@ public class Mac implements Cloneable { throw new IllegalStateException(); } if (input != null) { - spiImpl.engineUpdate(input, 0, input.length); + getSpi().engineUpdate(input, 0, input.length); } } @@ -305,7 +406,7 @@ public class Mac implements Cloneable { throw new IllegalStateException(); } if (input != null) { - spiImpl.engineUpdate(input); + getSpi().engineUpdate(input); } else { throw new IllegalArgumentException("input == null"); } @@ -327,7 +428,7 @@ public class Mac implements Cloneable { if (!isInitMac) { throw new IllegalStateException(); } - return spiImpl.engineDoFinal(); + return getSpi().engineDoFinal(); } /** @@ -362,11 +463,12 @@ public class Mac implements Cloneable { if ((outOffset < 0) || (outOffset >= output.length)) { throw new ShortBufferException("Incorrect outOffset: " + outOffset); } - int t = spiImpl.engineGetMacLength(); + MacSpi spi = getSpi(); + int t = spi.engineGetMacLength(); if (t > (output.length - outOffset)) { throw new ShortBufferException("Output buffer is short. Needed " + t + " bytes."); } - byte[] result = spiImpl.engineDoFinal(); + byte[] result = spi.engineDoFinal(); System.arraycopy(result, 0, output, outOffset, result.length); } @@ -390,10 +492,11 @@ public class Mac implements Cloneable { if (!isInitMac) { throw new IllegalStateException(); } + MacSpi spi = getSpi(); if (input != null) { - spiImpl.engineUpdate(input, 0, input.length); + spi.engineUpdate(input, 0, input.length); } - return spiImpl.engineDoFinal(); + return spi.engineDoFinal(); } /** @@ -404,7 +507,7 @@ public class Mac implements Cloneable { * initialized with different parameters. */ public final void reset() { - spiImpl.engineReset(); + getSpi().engineReset(); } /** @@ -416,7 +519,11 @@ public class Mac implements Cloneable { */ @Override public final Object clone() throws CloneNotSupportedException { - MacSpi newSpiImpl = (MacSpi)spiImpl.clone(); + MacSpi newSpiImpl = null; + final MacSpi spi = getSpi(); + if (spi != null) { + newSpiImpl = (MacSpi) spi.clone(); + } Mac mac = new Mac(newSpiImpl, this.provider, this.algorithm); mac.isInitMac = this.isInitMac; return mac; diff --git a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/MacTest.java b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/MacTest.java index aaf2a15..ddd0695 100644 --- a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/MacTest.java +++ b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/MacTest.java @@ -27,27 +27,26 @@ import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; +import java.security.PrivateKey; import java.security.Provider; import java.security.Security; import java.security.spec.PSSParameterSpec; import java.util.ArrayList; import java.util.Arrays; - import javax.crypto.Mac; import javax.crypto.MacSpi; import javax.crypto.SecretKey; import javax.crypto.ShortBufferException; import javax.crypto.spec.DHGenParameterSpec; - import javax.crypto.spec.SecretKeySpec; - import org.apache.harmony.crypto.tests.support.MyMacSpi; import org.apache.harmony.security.tests.support.SpiEngUtils; - import junit.framework.TestCase; - import junit.framework.Test; import junit.framework.TestSuite; +import libcore.java.security.StandardNames; +import libcore.javax.crypto.MockKey; +import libcore.javax.crypto.MockKey2; /** * Tests for Mac class constructors and methods @@ -766,15 +765,14 @@ public class MacTest extends TestCase { } MacSpi spi = new MyMacSpi(); Mac mac = new myMac(spi, defaultProvider, defaultAlgorithm); - assertEquals("Incorrect algorithm", mac.getAlgorithm(), - defaultAlgorithm); - assertEquals("Incorrect provider", mac.getProvider(), defaultProvider); + assertEquals("Incorrect algorithm", defaultAlgorithm, mac.getAlgorithm()); + assertEquals("Incorrect provider", defaultProvider, mac.getProvider()); try { mac.init(null, null); fail("Exception should be thrown because init(..) uses incorrect parameters"); } catch (Exception e) { } - assertEquals("Invalid mac length", mac.getMacLength(), 0); + assertEquals("Invalid mac length", 0, mac.getMacLength()); mac = new myMac(null, null, null); assertNull("Algorithm must be null", mac.getAlgorithm()); @@ -877,6 +875,127 @@ public class MacTest 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 testMac_getInstance_SuppliedProviderNotRegistered_Success() throws Exception { + Provider mockProvider = new MockProvider("MockProvider") { + public void setup() { + put("Mac.FOO", MockMacSpi.AllKeyTypes.class.getName()); + } + }; + + { + Mac s = Mac.getInstance("FOO", mockProvider); + s.init(new MockKey()); + assertEquals(mockProvider, s.getProvider()); + } + } + + public void testMac_getInstance_OnlyUsesSpecifiedProvider_SameNameAndClass_Success() + throws Exception { + Provider mockProvider = new MockProvider("MockProvider") { + public void setup() { + put("Mac.FOO", MockMacSpi.AllKeyTypes.class.getName()); + } + }; + + Security.addProvider(mockProvider); + try { + { + Provider mockProvider2 = new MockProvider("MockProvider") { + public void setup() { + put("Mac.FOO", MockMacSpi.AllKeyTypes.class.getName()); + } + }; + Mac s = Mac.getInstance("FOO", mockProvider2); + assertEquals(mockProvider2, s.getProvider()); + } + } finally { + Security.removeProvider(mockProvider.getName()); + } + } + + public void testMac_getInstance_DelayedInitialization_KeyType() throws Exception { + Provider mockProviderSpecific = new MockProvider("MockProviderSpecific") { + public void setup() { + put("Mac.FOO", MockMacSpi.SpecificKeyTypes.class.getName()); + put("Mac.FOO SupportedKeyClasses", MockKey.class.getName()); + } + }; + Provider mockProviderSpecific2 = new MockProvider("MockProviderSpecific2") { + public void setup() { + put("Mac.FOO", MockMacSpi.SpecificKeyTypes2.class.getName()); + put("Mac.FOO SupportedKeyClasses", MockKey2.class.getName()); + } + }; + Provider mockProviderAll = new MockProvider("MockProviderAll") { + public void setup() { + put("Mac.FOO", MockMacSpi.AllKeyTypes.class.getName()); + } + }; + + Security.addProvider(mockProviderSpecific); + Security.addProvider(mockProviderSpecific2); + Security.addProvider(mockProviderAll); + + try { + { + Mac s = Mac.getInstance("FOO"); + s.init(new MockKey()); + assertEquals(mockProviderSpecific, s.getProvider()); + + try { + s.init(new MockKey2()); + assertEquals(mockProviderSpecific2, s.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"); + } + } + } + + { + Mac s = Mac.getInstance("FOO"); + s.init(new PrivateKey() { + @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, s.getProvider()); + } + + { + Mac s = Mac.getInstance("FOO"); + assertEquals(mockProviderSpecific, s.getProvider()); + } + } finally { + Security.removeProvider(mockProviderSpecific.getName()); + Security.removeProvider(mockProviderSpecific2.getName()); + Security.removeProvider(mockProviderAll.getName()); + } + } + public static Test suite() { return new TestSuite(MacTest.class); } diff --git a/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/MockMacSpi.java b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/MockMacSpi.java new file mode 100644 index 0000000..6a28fb3 --- /dev/null +++ b/luni/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/MockMacSpi.java @@ -0,0 +1,82 @@ +/* + * Copyright 2014 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 org.apache.harmony.crypto.tests.javax.crypto; + +import java.security.InvalidKeyException; +import java.security.InvalidParameterException; +import java.security.Key; +import java.security.spec.AlgorithmParameterSpec; +import javax.crypto.MacSpi; +import libcore.javax.crypto.MockKey; +import libcore.javax.crypto.MockKey2; + +public class MockMacSpi extends MacSpi { + public static class SpecificKeyTypes extends MockMacSpi { + @Override + public void checkKeyType(Key key) throws InvalidKeyException { + if (!(key instanceof MockKey)) { + throw new InvalidKeyException("Must be MockKey!"); + } + } + } + + public static class SpecificKeyTypes2 extends MockMacSpi { + @Override + public void checkKeyType(Key key) throws InvalidKeyException { + if (!(key instanceof MockKey2)) { + throw new InvalidKeyException("Must be MockKey2!"); + } + } + } + + public static class AllKeyTypes extends MockMacSpi { + } + + public void checkKeyType(Key key) throws InvalidKeyException { + } + + @Override + protected int engineGetMacLength() { + throw new UnsupportedOperationException(); + } + + @Override + protected void engineInit(Key key, AlgorithmParameterSpec params) throws InvalidKeyException, + InvalidParameterException { + checkKeyType(key); + } + + @Override + protected void engineUpdate(byte input) { + throw new UnsupportedOperationException(); + } + + @Override + protected void engineUpdate(byte[] input, int offset, int len) { + throw new UnsupportedOperationException(); + } + + @Override + protected byte[] engineDoFinal() { + throw new UnsupportedOperationException(); + } + + @Override + protected void engineReset() { + throw new UnsupportedOperationException(); + } +} |