diff options
author | Kenny Root <kroot@google.com> | 2014-02-06 16:00:43 -0800 |
---|---|---|
committer | Kenny Root <kroot@google.com> | 2014-02-06 17:29:43 -0800 |
commit | 91e77f683cc56948748185ff821a9c2ee11540ba (patch) | |
tree | 3eee35afae8a2b9465d606af8d5dea5a4b687e86 /luni | |
parent | 14a26bd0975d4f986552b8f5aa34c6526ab4f6b7 (diff) | |
download | libcore-91e77f683cc56948748185ff821a9c2ee11540ba.zip libcore-91e77f683cc56948748185ff821a9c2ee11540ba.tar.gz libcore-91e77f683cc56948748185ff821a9c2ee11540ba.tar.bz2 |
Late binding: add support to Mac
This adds support for late binding (or delayed selection) to the
Signature class. This allows the selection of the KeyAgreementSpi based
on what kind of Key class is used to init(...)
Change-Id: I8a14ac138e09d5bf2b925b5fa288c9adab540b76
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(); + } +} |