diff options
Diffstat (limited to 'gnu')
36 files changed, 1298 insertions, 596 deletions
diff --git a/gnu/java/beans/DefaultExceptionListener.java b/gnu/java/beans/DefaultExceptionListener.java new file mode 100644 index 000000000..42b31fae8 --- /dev/null +++ b/gnu/java/beans/DefaultExceptionListener.java @@ -0,0 +1,66 @@ +/* gnu.java.beans.DefaultExceptionListener + Copyright (C) 2004, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.java.beans; + +import java.beans.ExceptionListener; + +/** The DefaultExceptionListener is the default implementation of the + * {@link ExceptionListener} interface. An instance of + * this class is used whenever the user provided no + * <code>ExceptionListener</code> instance on its own. + * + * <p>The implementation just writes the exception's message + * to <code>System.err</code> and is used by the {@link java.beans.Encoder} + * and the {@link java.beans.XMLDecoder}. + * </p> + * + * @author Robert Schuster (robertschuster@fsfe.org) + */ +public class DefaultExceptionListener implements ExceptionListener +{ + public final static DefaultExceptionListener INSTANCE + = new DefaultExceptionListener(); + + public void exceptionThrown(Exception e) + { + System.err.println("exception thrown: " + + e + " - message: " + + e.getMessage()); + } + +} diff --git a/gnu/java/security/der/DERValue.java b/gnu/java/security/der/DERValue.java index 9a597d724..d98ce78ec 100644 --- a/gnu/java/security/der/DERValue.java +++ b/gnu/java/security/der/DERValue.java @@ -38,6 +38,8 @@ exception statement from your version. */ package gnu.java.security.der; +import gnu.java.security.x509.Util; + import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -108,7 +110,9 @@ public class DERValue implements DER } catch (IOException ioe) { - encoded = new byte[0]; + IllegalArgumentException iae = new IllegalArgumentException (); + iae.initCause (ioe); + throw iae; } } return length; @@ -138,7 +142,9 @@ public class DERValue implements DER } catch (IOException ioe) { - encoded = new byte[0]; + IllegalArgumentException iae = new IllegalArgumentException (); + iae.initCause (ioe); + throw iae; } } return (byte[]) encoded.clone(); @@ -156,7 +162,9 @@ public class DERValue implements DER } catch (IOException ioe) { - encoded = new byte[0]; + IllegalArgumentException iae = new IllegalArgumentException (); + iae.initCause (ioe); + throw iae; } } return encoded.length; @@ -164,7 +172,18 @@ public class DERValue implements DER public String toString() { - return "DERValue [ tag=" + tag + ", class=" + tagClass + ", constructed=" - + constructed + ", value=" + value + " ]"; + String start = "DERValue ( ["; + if (tagClass == DER.UNIVERSAL) + start = start + "UNIVERSAL "; + else if (tagClass == DER.PRIVATE) + start = start + "PRIVATE "; + else if (tagClass == DER.APPLICATION) + start = start + "APPLICATION "; + start = start + tag + "] constructed=" + constructed + ", value="; + if (constructed) + start = start + "\n" + Util.hexDump(getEncoded(), "\t"); + else + start = start + value; + return start + " )"; } } diff --git a/gnu/java/security/der/DERWriter.java b/gnu/java/security/der/DERWriter.java index 78524fc94..4298151e0 100644 --- a/gnu/java/security/der/DERWriter.java +++ b/gnu/java/security/der/DERWriter.java @@ -84,6 +84,12 @@ public class DERWriter implements DER public static int write(OutputStream out, DERValue object) throws IOException { + if (DER.CONSTRUCTED_VALUE.equals (object.getValue ())) + { + out.write (object.getEncoded ()); + return object.getLength (); + } + out.write(object.getExternalTag()); Object value = object.getValue(); if (value == null) diff --git a/gnu/java/security/jce/sig/DSSKeyPairGeneratorSpi.java b/gnu/java/security/jce/sig/DSSKeyPairGeneratorSpi.java index 5cb1380d2..2f4d36d51 100644 --- a/gnu/java/security/jce/sig/DSSKeyPairGeneratorSpi.java +++ b/gnu/java/security/jce/sig/DSSKeyPairGeneratorSpi.java @@ -42,7 +42,10 @@ import gnu.java.security.Registry; import gnu.java.security.key.dss.DSSKeyPairGenerator; import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidParameterException; import java.security.SecureRandom; +import java.security.interfaces.DSAKeyPairGenerator; +import java.security.interfaces.DSAParams; import java.security.spec.AlgorithmParameterSpec; import java.security.spec.DSAParameterSpec; import java.util.HashMap; @@ -55,9 +58,10 @@ import java.util.HashMap; * a call to an <code>initialize()</code> method), the GNU Crypto provider * uses a default <i>modulus</i> size (keysize) of 1024 bits.<p> * - * @version $Revision: 1.1 $ + * @version $Revision: 1.1.2.1 $ */ -public class DSSKeyPairGeneratorSpi extends KeyPairGeneratorAdapter +public class DSSKeyPairGeneratorSpi extends KeyPairGeneratorAdapter implements + DSAKeyPairGenerator { // Constants and variables @@ -79,14 +83,7 @@ public class DSSKeyPairGeneratorSpi extends KeyPairGeneratorAdapter public void initialize(int keysize, SecureRandom random) { - HashMap attributes = new HashMap(); - attributes.put(DSSKeyPairGenerator.MODULUS_LENGTH, new Integer(keysize)); - if (random != null) - { - attributes.put(DSSKeyPairGenerator.SOURCE_OF_RANDOMNESS, random); - } - - adaptee.setup(attributes); + this.initialize(keysize, false, random); } public void initialize(AlgorithmParameterSpec params, SecureRandom random) @@ -96,9 +93,9 @@ public class DSSKeyPairGeneratorSpi extends KeyPairGeneratorAdapter if (params != null) { if (!(params instanceof DSAParameterSpec)) - { - throw new InvalidAlgorithmParameterException("params"); - } + throw new InvalidAlgorithmParameterException( + "Parameters argument is not a non-null instance, or " + + "sub-instance, of java.security.spec.DSAParameterSpec"); attributes.put(DSSKeyPairGenerator.DSS_PARAMETERS, params); } @@ -108,6 +105,61 @@ public class DSSKeyPairGeneratorSpi extends KeyPairGeneratorAdapter attributes.put(DSSKeyPairGenerator.SOURCE_OF_RANDOMNESS, random); } - adaptee.setup(attributes); + try + { + adaptee.setup(attributes); + } + catch (IllegalArgumentException x) + { + InvalidAlgorithmParameterException y = + new InvalidAlgorithmParameterException(); + y.initCause(x); + throw y; + } + } + + // java.security.interfaces.DSAKeyPairGenerator interface implementation ----- + + public void initialize(DSAParams params, SecureRandom random) + throws InvalidParameterException + { + if (params == null || !(params instanceof DSAParameterSpec)) + throw new InvalidParameterException( + "Parameters argument is either null or is not an instance, or " + + "sub-instance, of java.security.spec.DSAParameterSpec"); + DSAParameterSpec spec = (DSAParameterSpec) params; + try + { + this.initialize((AlgorithmParameterSpec) spec, random); + } + catch (InvalidAlgorithmParameterException x) + { + InvalidParameterException y = new InvalidParameterException(); + y.initCause(x); + throw y; + } + } + + public void initialize(int modlen, boolean genParams, SecureRandom random) + throws InvalidParameterException + { + HashMap attributes = new HashMap(); + attributes.put(DSSKeyPairGenerator.MODULUS_LENGTH, new Integer(modlen)); + if (random != null) + attributes.put(DSSKeyPairGenerator.SOURCE_OF_RANDOMNESS, random); + + attributes.put(DSSKeyPairGenerator.USE_DEFAULTS, + Boolean.valueOf(!genParams)); + attributes.put(DSSKeyPairGenerator.STRICT_DEFAULTS, Boolean.TRUE); + try + { + adaptee.setup(attributes); + } + catch (IllegalArgumentException x) + { + InvalidParameterException y = new InvalidParameterException(); + y.initCause(x); + throw y; + } } } diff --git a/gnu/java/security/jce/sig/KeyPairGeneratorAdapter.java b/gnu/java/security/jce/sig/KeyPairGeneratorAdapter.java index 21a596a5f..b1dab1de8 100644 --- a/gnu/java/security/jce/sig/KeyPairGeneratorAdapter.java +++ b/gnu/java/security/jce/sig/KeyPairGeneratorAdapter.java @@ -43,7 +43,7 @@ import gnu.java.security.key.KeyPairGeneratorFactory; import java.security.InvalidAlgorithmParameterException; import java.security.KeyPair; -import java.security.KeyPairGeneratorSpi; +import java.security.KeyPairGenerator; import java.security.SecureRandom; import java.security.spec.AlgorithmParameterSpec; @@ -64,9 +64,9 @@ import java.security.spec.AlgorithmParameterSpec; * Crypto provider uses a default <i>modulus</i> size (keysize) of 1024 bits for * the DSS (Digital Signature Standard) a.k.a <i>DSA</i>.<p> * - * @version $Revision: 1.1 $ + * @version $Revision: 1.1.2.1 $ */ -abstract class KeyPairGeneratorAdapter extends KeyPairGeneratorSpi +abstract class KeyPairGeneratorAdapter extends KeyPairGenerator { // Constants and variables @@ -85,7 +85,7 @@ abstract class KeyPairGeneratorAdapter extends KeyPairGeneratorSpi */ protected KeyPairGeneratorAdapter(String kpgName) { - super(); + super(kpgName); this.adaptee = KeyPairGeneratorFactory.getInstance(kpgName); } diff --git a/gnu/java/security/key/dss/DSSKeyPairGenerator.java b/gnu/java/security/key/dss/DSSKeyPairGenerator.java index 3f9c80625..16af5e048 100644 --- a/gnu/java/security/key/dss/DSSKeyPairGenerator.java +++ b/gnu/java/security/key/dss/DSSKeyPairGenerator.java @@ -41,6 +41,7 @@ package gnu.java.security.key.dss; import gnu.java.security.Registry; import gnu.java.security.hash.Sha160; import gnu.java.security.key.IKeyPairGenerator; +import gnu.java.security.util.PRNG; import java.io.PrintWriter; import java.math.BigInteger; @@ -88,10 +89,55 @@ public class DSSKeyPairGenerator implements IKeyPairGenerator /** Property name of the length (Integer) of the modulus (p) of a DSS key. */ public static final String MODULUS_LENGTH = "gnu.crypto.dss.L"; - /** Property name of the Boolean indicating wether or not to use defaults. */ + /** + * Property name of the Boolean indicating wether or not to use default pre- + * computed values of <code>p</code>, <code>q</code> and <code>g</code> for + * a given modulus length. The ultimate behaviour of this generator with + * regard to using pre-computed parameter sets will depend on the value of + * this property and of the following one {@link #STRICT_DEFAULTS}: + * + * <ol> + * <li>If this property is {@link Boolean#FALSE} then this generator + * will accept being setup for generating parameters for any modulus length + * provided the modulus length is between <code>512</code> and + * <code>1024</code>, and is of the form <code>512 + 64 * n</code>. In + * addition, a new paramter set will always be generated; i.e. no pre- + * computed values are used.</li> + * + * <li>If this property is {@link Boolean#TRUE} and the value of + * {@link #STRICT_DEFAULTS} is also {@link Boolean#TRUE} then this generator + * will only accept being setup for generating parameters for modulus + * lengths of <code>512</code>, <code>768</code> and <code>1024</code>. Any + * other value, of the modulus length, even if between <code>512</code> and + * <code>1024</code>, and of the form <code>512 + 64 * n</code>, will cause + * an {@link IllegalArgumentException} to be thrown. When those modulus + * length (<code>512</code>, <code>768</code>, and <code>1024</code>) are + * specified, the paramter set is always the same.</li> + * + * <li>Finally, if this property is {@link Boolean#TRUE} and the value of + * {@link #STRICT_DEFAULTS} is {@link Boolean#FALSE} then this generator + * will behave as in point 1 above, except that it will use pre-computed + * values when possible; i.e. the modulus length is one of <code>512</code>, + * <code>768</code>, or <code>1024</code>.</li> + * </ol> + * + * The default value of this property is {@link Boolean#TRUE}. + */ public static final String USE_DEFAULTS = "gnu.crypto.dss.use.defaults"; /** + * Property name of the Boolean indicating wether or not to generate new + * parameters, even if the modulus length <i>L</i> is not one of the pre- + * computed defaults (value {@link Boolean#FALSE}), or throw an exception + * (value {@link Boolean#TRUE}) -- the exception in this case is an + * {@link IllegalArgumentException}. The default value for this property is + * {@link Boolean#FALSE}. The ultimate behaviour of this generator will + * depend on the values of this and {@link #USE_DEFAULTS} properties -- see + * {@link #USE_DEFAULTS} for more information. + */ + public static final String STRICT_DEFAULTS = "gnu.crypto.dss.strict.defaults"; + + /** * Property name of an optional {@link SecureRandom} instance to use. The * default is to use a classloader singleton from {@link PRNG}. */ @@ -181,6 +227,9 @@ public class DSSKeyPairGenerator implements IKeyPairGenerator private BigInteger XKEY; + /** Our default source of randomness. */ + private PRNG prng = null; + // Constructor(s) // ------------------------------------------------------------------------- @@ -222,6 +271,10 @@ public class DSSKeyPairGenerator implements IKeyPairGenerator useDefaults = Boolean.TRUE; } + Boolean strictDefaults = (Boolean) attributes.get(STRICT_DEFAULTS); + if (strictDefaults == null) + strictDefaults = Boolean.FALSE; + // are we given a set of DSA params or we shall use/generate our own? DSAParameterSpec params = (DSAParameterSpec) attributes.get(DSS_PARAMETERS); if (params != null) @@ -250,9 +303,16 @@ public class DSSKeyPairGenerator implements IKeyPairGenerator g = KEY_PARAMS_1024.getG(); break; default: - p = null; - q = null; - g = null; + if (strictDefaults.equals(Boolean.TRUE)) + throw new IllegalArgumentException( + "Does not provide default parameters for " + L + + "-bit modulus length"); + else + { + p = null; + q = null; + g = null; + } } } else @@ -353,8 +413,14 @@ public class DSSKeyPairGenerator implements IKeyPairGenerator rnd.nextBytes(buffer); } else - { - new SecureRandom ().nextBytes(buffer); - } + getDefaultPRNG().nextBytes(buffer); + } + + private PRNG getDefaultPRNG() + { + if (prng == null) + prng = PRNG.getInstance(); + + return prng; } } diff --git a/gnu/java/security/key/dss/FIPS186.java b/gnu/java/security/key/dss/FIPS186.java index 796f24bd3..c796bab2c 100644 --- a/gnu/java/security/key/dss/FIPS186.java +++ b/gnu/java/security/key/dss/FIPS186.java @@ -39,6 +39,7 @@ exception statement from your version. */ package gnu.java.security.key.dss; import gnu.java.security.hash.Sha160; +import gnu.java.security.util.PRNG; import gnu.java.security.util.Prime2; import java.math.BigInteger; @@ -53,7 +54,7 @@ import java.security.SecureRandom; * Standard (DSS)</a>, Federal Information Processing Standards Publication 186. * National Institute of Standards and Technology. * - * @version $Revision: 1.1 $ + * @version $Revision: 1.1.2.1 $ */ public class FIPS186 { @@ -87,6 +88,9 @@ public class FIPS186 /** The optional {@link SecureRandom} instance to use. */ private SecureRandom rnd = null; + /** Our default source of randomness. */ + private PRNG prng = null; + // Constructor(s) // ------------------------------------------------------------------------- @@ -126,7 +130,7 @@ public class FIPS186 * * The algorithm used to find these primes is as described in FIPS-186, * section 2.2: GENERATION OF PRIMES. This prime generation scheme starts by - * using the {@link gnu.crypto.hash.Sha160} and a user supplied <i>SEED</i> + * using the {@link Sha160} and a user supplied <i>SEED</i> * to construct a prime, <code>q</code>, in the range 2<sup>159</sup> < q * < 2<sup>160</sup>. Once this is accomplished, the same <i>SEED</i> * value is used to construct an <code>X</code> in the range <code>2<sup>L-1 @@ -279,8 +283,14 @@ public class FIPS186 rnd.nextBytes(buffer); } else - { - new SecureRandom ().nextBytes(buffer); - } + getDefaultPRNG().nextBytes(buffer); + } + + private PRNG getDefaultPRNG() + { + if (prng == null) + prng = PRNG.getInstance(); + + return prng; } } diff --git a/gnu/java/security/key/rsa/RSAKeyPairGenerator.java b/gnu/java/security/key/rsa/RSAKeyPairGenerator.java index a9738e751..360036723 100644 --- a/gnu/java/security/key/rsa/RSAKeyPairGenerator.java +++ b/gnu/java/security/key/rsa/RSAKeyPairGenerator.java @@ -40,6 +40,7 @@ package gnu.java.security.key.rsa; import gnu.java.security.Registry; import gnu.java.security.key.IKeyPairGenerator; +import gnu.java.security.util.PRNG; import gnu.java.security.util.Prime2; import java.math.BigInteger; @@ -109,6 +110,9 @@ public class RSAKeyPairGenerator implements IKeyPairGenerator /** The optional {@link SecureRandom} instance to use. */ private SecureRandom rnd = null; + /** Our default source of randomness. */ + private PRNG prng = null; + // Constructor(s) // ------------------------------------------------------------------------- @@ -229,8 +233,14 @@ public class RSAKeyPairGenerator implements IKeyPairGenerator rnd.nextBytes(buffer); } else - { - new SecureRandom ().nextBytes(buffer); - } + getDefaultPRNG().nextBytes(buffer); + } + + private PRNG getDefaultPRNG() + { + if (prng == null) + prng = PRNG.getInstance(); + + return prng; } } diff --git a/gnu/java/security/sig/BaseSignature.java b/gnu/java/security/sig/BaseSignature.java index f82346feb..5b43f2c27 100644 --- a/gnu/java/security/sig/BaseSignature.java +++ b/gnu/java/security/sig/BaseSignature.java @@ -41,10 +41,10 @@ package gnu.java.security.sig; import gnu.java.security.hash.IMessageDigest; import gnu.java.security.prng.IRandom; import gnu.java.security.prng.LimitReachedException; +import gnu.java.security.util.PRNG; import java.security.PrivateKey; import java.security.PublicKey; -import java.security.SecureRandom; import java.util.Map; import java.util.Random; @@ -76,6 +76,9 @@ public abstract class BaseSignature implements ISignature /** The optional {@link IRandom} instance to use. */ private IRandom irnd; + /** Our default source of randomness. */ + private PRNG prng = null; + // Constructor(s) // ------------------------------------------------------------------------- @@ -224,9 +227,7 @@ public abstract class BaseSignature implements ISignature } } else - { - new SecureRandom ().nextBytes(buffer); - } + getDefaultPRNG().nextBytes(buffer); } private void setup(Map attributes) @@ -244,4 +245,12 @@ public abstract class BaseSignature implements ISignature irnd = (IRandom) obj; } } + + private PRNG getDefaultPRNG() + { + if (prng == null) + prng = PRNG.getInstance(); + + return prng; + } } diff --git a/gnu/java/security/sig/rsa/EME_PKCS1_V1_5.java b/gnu/java/security/sig/rsa/EME_PKCS1_V1_5.java index d6bbfe56b..efe580d51 100644 --- a/gnu/java/security/sig/rsa/EME_PKCS1_V1_5.java +++ b/gnu/java/security/sig/rsa/EME_PKCS1_V1_5.java @@ -40,9 +40,9 @@ package gnu.java.security.sig.rsa; import gnu.java.security.prng.IRandom; import gnu.java.security.prng.LimitReachedException; +import gnu.java.security.util.PRNG; import java.io.ByteArrayOutputStream; -import java.security.SecureRandom; import java.security.interfaces.RSAKey; import java.util.Random; @@ -70,6 +70,9 @@ public class EME_PKCS1_V1_5 private ByteArrayOutputStream baos = new ByteArrayOutputStream(); + /** Our default source of randomness. */ + private PRNG prng = PRNG.getInstance(); + // Constructor(s) // ------------------------------------------------------------------------- @@ -128,8 +131,7 @@ public class EME_PKCS1_V1_5 final byte[] PS = new byte[k - M.length - 3]; // FIXME. This should be configurable, somehow. - SecureRandom rnd = new SecureRandom (); - rnd.nextBytes(PS); + prng.nextBytes(PS); int i = 0; for (; i < PS.length; i++) { @@ -300,6 +302,5 @@ public class EME_PKCS1_V1_5 baos.reset(); return result; - } } diff --git a/gnu/java/security/sig/rsa/RSA.java b/gnu/java/security/sig/rsa/RSA.java index 4ef7232f1..7d1707e19 100644 --- a/gnu/java/security/sig/rsa/RSA.java +++ b/gnu/java/security/sig/rsa/RSA.java @@ -39,12 +39,11 @@ exception statement from your version. */ package gnu.java.security.sig.rsa; import gnu.java.security.Properties; -import gnu.java.security.key.rsa.GnuRSAKey; +import gnu.java.security.util.PRNG; import java.math.BigInteger; import java.security.PrivateKey; import java.security.PublicKey; -import java.security.SecureRandom; import java.security.interfaces.RSAPrivateCrtKey; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; @@ -79,6 +78,9 @@ public class RSA private static final BigInteger ONE = BigInteger.ONE; + /** Our default source of randomness. */ + private static final PRNG prng = PRNG.getInstance(); + // Constructor(s) // ------------------------------------------------------------------------- @@ -340,16 +342,15 @@ public class RSA final int upper = (N.bitLength() + 7) / 8; final int lower = upper / 2; final byte[] bl = new byte[1]; - SecureRandom rnd = new SecureRandom (); int b; do { - rnd.nextBytes(bl); + prng.nextBytes(bl); b = bl[0] & 0xFF; } while (b < lower || b > upper); final byte[] buffer = new byte[b]; // 256-bit MPI - rnd.nextBytes(buffer); + prng.nextBytes(buffer); return new BigInteger(1, buffer); } } diff --git a/gnu/java/security/util/PRNG.java b/gnu/java/security/util/PRNG.java new file mode 100644 index 000000000..138cc6bcb --- /dev/null +++ b/gnu/java/security/util/PRNG.java @@ -0,0 +1,156 @@ +/* PRNG.java -- A Utility methods for default source of randomness + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.security.util; + +import java.util.HashMap; + +import gnu.java.security.prng.IRandom; +import gnu.java.security.prng.LimitReachedException; +import gnu.java.security.prng.MDGenerator; + +/** + * A useful hash-based (SHA) pseudo-random number generator used + * throughout this library. + * + * @see MDGenerator + */ +public class PRNG +{ + // Constans and fields + // -------------------------------------------------------------------------- + + /** The underlying {@link IRandom}. */ + private IRandom delegate; + + // Constructor(s) + // -------------------------------------------------------------------------- + + /** + * Private constructor to enforce using the Factory method. + * + * @param delegate + * the undelying {@link IRandom} object used. + */ + private PRNG(IRandom delegate) + { + super(); + + this.delegate = delegate; + } + + // Class methods + // -------------------------------------------------------------------------- + + public static final PRNG getInstance() + { + IRandom delegate = new MDGenerator(); + try + { + HashMap map = new HashMap(); + // initialise it with a seed + long t = System.currentTimeMillis(); + byte[] seed = new byte[] { + (byte) (t >>> 56), (byte) (t >>> 48), + (byte) (t >>> 40), (byte) (t >>> 32), + (byte) (t >>> 24), (byte) (t >>> 16), + (byte) (t >>> 8), (byte) t}; + map.put(MDGenerator.SEEED, seed); + delegate.init(map); // default is to use SHA-1 hash + } + catch (Exception x) + { + throw new ExceptionInInitializerError(x); + } + + return new PRNG(delegate); + } + + // Instance methods + // -------------------------------------------------------------------------- + + /** + * Completely fills the designated <code>buffer</code> with random data + * generated by the underlying delegate. + * + * @param buffer + * the place holder of random bytes generated by the underlying + * delegate. On output, the contents of <code>buffer</code> are + * replaced with pseudo-random data, iff the <code>buffer</code> + * size is not zero. + */ + public void nextBytes(byte[] buffer) + { + nextBytes(buffer, 0, buffer.length); + } + + /** + * Fills the designated <code>buffer</code>, starting from byte at position + * <code>offset</code> with, at most, <code>length</code> bytes of random + * data generated by the underlying delegate. + * + * @see IRandom#nextBytes + */ + public void nextBytes(byte[] buffer, int offset, int length) + { + try + { + delegate.nextBytes(buffer, offset, length); + } + catch (LimitReachedException x) // re-initialise with a seed + { + try + { + HashMap map = new HashMap(); + long t = System.currentTimeMillis(); + byte[] seed = new byte[] { + (byte)(t >>> 56), (byte)(t >>> 48), + (byte)(t >>> 40), (byte)(t >>> 32), + (byte)(t >>> 24), (byte)(t >>> 16), + (byte)(t >>> 8), (byte) t }; + map.put(MDGenerator.SEEED, seed); + delegate.init(map); // default is to use SHA-1 hash + delegate.nextBytes(buffer, offset, length); + } + catch (Exception y) + { + throw new ExceptionInInitializerError(y); + } + } + } +} diff --git a/gnu/java/security/util/Prime2.java b/gnu/java/security/util/Prime2.java index bf8df55ad..6e46f5fca 100644 --- a/gnu/java/security/util/Prime2.java +++ b/gnu/java/security/util/Prime2.java @@ -38,13 +38,10 @@ exception statement from your version. */ package gnu.java.security.util; -import gnu.java.security.Properties; - import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.math.BigInteger; import java.util.Map; -import java.util.Random; import java.util.WeakHashMap; /** @@ -189,7 +186,7 @@ public class Prime2 /** * <p>Java port of Colin Plumb primality test (Euler Criterion) * implementation for a base of 2 --from bnlib-1.1 release, function - * primeTest() in prime.c. this is his comments; (bn is our w).</p> + * primeTest() in prime.c. this is his comments.</p> * * <p>"Now, check that bn is prime. If it passes to the base 2, it's prime * beyond all reasonable doubt, and everything else is just gravy, but it @@ -234,86 +231,61 @@ public class Prime2 * All primes <code>== 1 (mod 4)</code> can be expressed as <code>a^2 + * (2*b)^2</code>, but I see no cheap way to evaluate this condition."</p> * - * @param w the number to test. + * @param bn the number to test. * @return <code>true</code> iff the designated number passes Euler criterion * as implemented by Colin Plumb in his <i>bnlib</i> version 1.1. */ - - // FIXME this method incorrectly returns false for certain primes, notably - // 38737, 61681, 65537, 229153, and 274177. - public static boolean passEulerCriterion(final BigInteger w) + public static boolean passEulerCriterion(final BigInteger bn) { - // first check if it's already a known prime - WeakReference obj = (WeakReference) knownPrimes.get(w); - if (obj != null && w.equals(obj.get())) - { - if (DEBUG && debuglevel > 4) - { - debug("found in known primes"); - } - return true; - } - - BigInteger w_minus_one = w.subtract(ONE); - BigInteger e = w_minus_one; + BigInteger bn_minus_one = bn.subtract(ONE); + BigInteger e = bn_minus_one; // l is the 3 least-significant bits of e int l = e.and(BigInteger.valueOf(7L)).intValue(); int j = 1; // Where to start in prime array for strong prime tests - BigInteger A; + BigInteger a; int k; - if ((l & 7) != 0) + if (l != 0) { e = e.shiftRight(1); - A = TWO.modPow(e, w); - if ((l & 7) == 6) - { // bn == 7 mod 8, expect +1 - if (A.bitCount() != 1) + a = TWO.modPow(e, bn); + if (l == 6) // bn == 7 mod 8, expect +1 + { + if (a.bitLength() != 1) { - if (DEBUG && debuglevel > 4) - { - debug(w.toString(16) + " fails Euler criterion #1..."); - } + debugBI("Fails Euler criterion #1", bn); return false; // Not prime } k = 1; } - else - { // bn == 3 or 5 mod 8, expect -1 == bn-1 - A = A.add(ONE); - if (!A.equals(w)) + else // bn == 3 or 5 mod 8, expect -1 == bn-1 + { + a = a.add(ONE); + if (a.compareTo(bn) != 0) { - if (DEBUG && debuglevel > 4) - { - debug(w.toString(16) + " fails Euler criterion #2..."); - } + debugBI("Fails Euler criterion #2", bn); return false; // Not prime } k = 1; - if ((l & 4) != 0) - { // bn == 5 mod 8, make odd for strong tests + if ((l & 4) != 0) // bn == 5 mod 8, make odd for strong tests + { e = e.shiftRight(1); k = 2; } } } - else - { // bn == 1 mod 8, expect 2^((bn-1)/4) == +/-1 mod bn + else // bn == 1 mod 8, expect 2^((bn-1)/4) == +/-1 mod bn + { e = e.shiftRight(2); - A = TWO.modPow(e, w); - if (A.bitCount() == 1) - { - j = 0; // Re-do strong prime test to base 2 - } + a = TWO.modPow(e, bn); + if (a.bitLength() == 1) + j = 0; // Re-do strong prime test to base 2 else { - A = A.add(ONE); - if (!A.equals(w)) + a = a.add(ONE); + if (a.compareTo(bn) != 0) { - if (DEBUG && debuglevel > 4) - { - debug(w.toString(16) + " fails Euler criterion #3..."); - } + debugBI("Fails Euler criterion #3", bn); return false; // Not prime } } @@ -329,187 +301,38 @@ public class Prime2 // when the previous test was as good as a strong prime test, but 1/8 of // the time, j = 0 because the strong prime test to the base 2 needs to // be re-done. - //for (int i = j; i < SMALL_PRIME_COUNT; i++) { - for (int i = j; i < 13; i++) - { // try only the first 13 primes - A = SMALL_PRIME[i]; - A = A.modPow(e, w); - if (A.bitCount() == 1) - { - continue; // Passed this test - } + for (int i = j; i < 7; i++) // try only the first 7 primes + { + a = SMALL_PRIME[i]; + a = a.modPow(e, bn); + if (a.bitLength() == 1) + continue; // Passed this test + l = k; while (true) { - // A = A.add(ONE); - // if (A.equals(w)) { // Was result bn-1? - if (A.equals(w_minus_one)) - { // Was result bn-1? - break; // Prime - } - if (--l == 0) - { // Reached end, not -1? luck? - if (DEBUG && debuglevel > 4) - { - debug(w.toString(16) + " fails Euler criterion #4..."); - } +// a = a.add(ONE); +// if (a.compareTo(w) == 0) { // Was result bn-1? + if (a.compareTo(bn_minus_one) == 0) // Was result bn-1? + break; // Prime + + if (--l == 0) // Reached end, not -1? luck? + { + debugBI("Fails Euler criterion #4", bn); return false; // Failed, not prime } // This portion is executed, on average, once - // A = A.subtract(ONE); // Put a back where it was - A = A.modPow(TWO, w); - if (A.bitCount() == 1) +// a = a.subtract(ONE); // Put a back where it was + a = a.modPow(TWO, bn); + if (a.bitLength() == 1) { - if (DEBUG && debuglevel > 4) - { - debug(w.toString(16) + " fails Euler criterion #5..."); - } + debugBI("Fails Euler criterion #5", bn); return false; // Failed, not prime } } // It worked (to the base primes[i]) } - if (DEBUG && debuglevel > 4) - { - debug(w.toString(16) + " passes Euler criterion..."); - } - - // store it in the known primes weak hash-map - knownPrimes.put(w, new WeakReference(w)); - - return true; - } - - /** - * <p>Checks Fermat's Little Theorem for base <i>b</i>; i.e. - * <code><i>b</i>**(w-1) == 1 (mod w)</code>.</p> - * - * @param w the number to test. - * @param t the number of random bases to test. - * @return <code>true</code> iff <code><i>b</i>**(w-1) == 1 (mod w)</code>, - * for some <i>b</i>. - */ - public static boolean passFermatLittleTheorem(final BigInteger w, int t) - { - final BigInteger w_minus_one = w.subtract(ONE); - if (t <= 0) - { - t = 10; // XXX - } - if (!TWO.modPow(w_minus_one, w).equals(ONE)) - { - if (DEBUG && debuglevel > 4) - { - debug(w.toString(16) + " fails Fermat's little theorem for base 2"); - } - return false; - } - for (int i = 0; i < t; i++) - { - byte[] buf = new byte[(w.bitLength() + 7) / 8 - 1]; - BigInteger base = null; - do - { - new Random ().nextBytes(buf); - base = new BigInteger(1, buf); - } - while (base.compareTo(TWO) < 0 || base.compareTo(w_minus_one) > 0); - if (!base.modPow(w_minus_one, w).equals(ONE)) - { - if (DEBUG && debuglevel > 4) - { - debug(w.toString(16) - + " fails Fermat's little theorem for base " - + base.toString(16)); - } - return false; - } - } - if (DEBUG && debuglevel > 4) - { - debug(w.toString(16) + " passes Fermat's little theorem for " + t - + " bases..."); - } - return true; - } - - /** - * <p>Applies the Miller-Rabin strong probabilistic primality test.</p> - * - * <p>The HAC (Handbook of Applied Cryptography), Alfred Menezes & al. Note - * 4.57 states that for <code>q</code>, <code>n=18</code> is enough while - * for <code>p</code>, <code>n=6</code> (512 bits) or <code>n=3</cdoe> (1024 - * bits) are enough to yield <i>robust</i> primality tests. The values used - * are from table 4.4 given in Note 4.49.</p> - * - * @param w the number to test. - * @return <code>true</code> iff the designated number passes the Miller- - * Rabin probabilistic primality test for a computed number of rounds. - */ - public static boolean passMillerRabin(BigInteger n, int t) - { - int nbytes = (n.bitLength() + 7) / 8; - byte[] ab = new byte[nbytes]; - - // 1. Write n - 1 = 2^s * r. - BigInteger n_minus_1 = n.subtract(ONE); - BigInteger r = n_minus_1; - int s = 0; - while (!r.testBit(0)) - { - r = r.shiftRight(1); - s++; - } - - // 2. For i from 1 to t, do: - for (int i = 0; i < t; i++) - { - - // 2.1 Choose a random integer a, 2 <= a <= n - 2. - BigInteger a; - do - { - new Random ().nextBytes(ab); - a = new BigInteger(1, ab); - } - while (a.compareTo(TWO) < 0 || a.compareTo(n) > 0); - - // 2.2 Compute y = a^r mod n. - BigInteger y = a.modPow(r, n); - - // If y != 1 and y != n - 1, then: - if (!y.equals(ONE) && !y.equals(n_minus_1)) - { - for (int j = 1; j < s - 1 && !y.equals(n_minus_1); j++) - { - // Compute y = y^2 mod n. - y = y.modPow(TWO, n); - - // If y = 1 return "composite" - if (y.equals(ONE)) - { - if (DEBUG && debuglevel > 4) - { - debug(n.toString(16) + " fails Miller-Rabin test..."); - } - return false; - } - } - // If y != n - 1 return "composite" - if (!y.equals(n_minus_1)) - { - if (DEBUG && debuglevel > 4) - { - debug(n.toString(16) + " fails Miller-Rabin test..."); - } - return false; - } - } - } - if (DEBUG && debuglevel > 4) - { - debug(n.toString(16) + " passes Miller-Rabin test..."); - } + debugBI("Passes Euler criterion", bn); return true; } @@ -519,114 +342,76 @@ public class Prime2 } /** - * <p>This implementation does not rely solely on the Miller-Rabin strong - * probabilistic primality test to claim the primality of the designated - * number. It instead, tries dividing the designated number by the first 1000 - * small primes, and if no divisor was found, invokes a port of Colin Plumb's - * implementation of the Euler Criterion, then tries the Miller-Rabin test.</p> + * Wrapper around {@link BigInteger#isProbablePrime(int)} with few pre-checks. * * @param w the integer to test. * @param certainty the certainty with which to compute the test. - * @param doMillerRabin if <code>true</code> and the designated integer was - * already found to be a probable prime, then also do a Miller-Rabin test. - * @return <code>true</code> iff the designated number has no small prime - * divisor passes the Euler criterion, and optionally a Miller-Rabin test. */ public static boolean isProbablePrime(BigInteger w, int certainty) { // Nonnumbers are not prime. if (w == null) - { return false; - } // eliminate trivial cases when w == 0 or 1 if (w.equals(ZERO) || w.equals(ONE)) - { return false; - } // Test if w is a known small prime. for (int i = 0; i < SMALL_PRIME_COUNT; i++) - { if (w.equals(SMALL_PRIME[i])) { if (DEBUG && debuglevel > 4) - { debug(w.toString(16) + " is a small prime"); - } return true; } - } - // trial division with first 1000 primes - if (hasSmallPrimeDivisor(w)) + // Check if it's already a known prime + WeakReference obj = (WeakReference) knownPrimes.get(w); + if (obj != null && w.equals(obj.get())) { if (DEBUG && debuglevel > 4) - { - debug(w.toString(16) + " has a small prime divisor. Rejected..."); - } - return false; + debug("found in known primes"); + return true; } - // Do a check with Fermat's little theorem. - // if (passFermatLittleTheorem(w, certainty)) { - // if (DEBUG && debuglevel > 4) { - // debug(w.toString(16)+" passes Fermat's little theorem..."); - // } - // } else { - // if (DEBUG && debuglevel > 4) { - // debug(w.toString(16)+" fails Fermat's little theorem. Rejected..."); - // } - // return false; - // } - - // Euler's criterion. - // if (passEulerCriterion(w)) { - // if (DEBUG && debuglevel > 4) { - // debug(w.toString(16)+" passes Euler's criterion..."); - // } - // } else { - // if (DEBUG && debuglevel > 4) { - // debug(w.toString(16)+" fails Euler's criterion. Rejected..."); - // } - // return false; - // } - - // Miller-Rabin probabilistic primality test. - if (passMillerRabin(w, certainty)) - { - if (DEBUG && debuglevel > 4) - { - debug(w.toString(16) + " passes Miller-Rabin PPT..."); - } - } - else + // trial division with first 1000 primes + if (hasSmallPrimeDivisor(w)) { if (DEBUG && debuglevel > 4) - { - debug(w.toString(16) + " fails Miller-Rabin PPT. Rejected..."); - } + debug(w.toString(16) + " has a small prime divisor. Rejected..."); return false; } - if (DEBUG && debuglevel > 4) - { - debug(w.toString(16) + " is probable prime. Accepted..."); - } +// Euler's criterion. +// if (passEulerCriterion(w)) { +// if (DEBUG && debuglevel > 4) { +// debug(w.toString(16)+" passes Euler's criterion..."); +// } +// } else { +// if (DEBUG && debuglevel > 4) { +// debug(w.toString(16)+" fails Euler's criterion. Rejected..."); +// } +// return false; +// } +// +// if (DEBUG && debuglevel > 4) +// { +// debug(w.toString(16) + " is probable prime. Accepted..."); +// } + + boolean result = w.isProbablePrime(certainty); + if (result && certainty > 0) // store it in the known primes weak hash-map + knownPrimes.put(w, new WeakReference(w)); + + return result; + } - // now compare to JDK primality test - if (DEBUG && debuglevel > 0 && !w.isProbablePrime(certainty)) - { - System.err.println("The gnu.crypto library and the JDK disagree on " - + "whether 0x" + w.toString(16) - + " is a probable prime or not."); - System.err.println("While this library claims it is, the JDK claims" - + " the opposite."); - System.err.println("Please contact the maintainer of this library, " - + "and provide this message for further investigation. TIA"); - } + // helper methods ----------------------------------------------------------- - return true; + private static final void debugBI(String msg, BigInteger bn) + { + if (DEBUG && debuglevel > 4) + debug("*** " + msg + ": 0x" + bn.toString(16)); } } diff --git a/gnu/java/security/x509/ext/GeneralNames.java b/gnu/java/security/x509/ext/GeneralNames.java index e92aedaef..dae94cd9f 100644 --- a/gnu/java/security/x509/ext/GeneralNames.java +++ b/gnu/java/security/x509/ext/GeneralNames.java @@ -52,6 +52,8 @@ import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import javax.security.auth.x500.X500Principal; + public class GeneralNames { @@ -81,12 +83,14 @@ public class GeneralNames if (!nameList.isConstructed()) throw new IOException("malformed GeneralNames"); int len = 0; + int i = 0; while (len < nameList.getLength()) { DERValue name = der.read(); List namePair = new ArrayList(2); - if (name.getTagClass() != DER.APPLICATION) - throw new IOException("malformed GeneralName"); + int tagClass = name.getTagClass(); + if (tagClass != DER.CONTEXT) + throw new IOException("malformed GeneralName: Tag class is " + tagClass); namePair.add(new Integer(name.getTag())); DERValue val = null; switch (name.getTag()) @@ -99,6 +103,15 @@ public class GeneralNames break; case OTHER_NAME: + // MUST return the encoded bytes of the OID/OctetString sequence + byte[] anotherName = name.getEncoded(); + anotherName[0] = (byte) (DER.CONSTRUCTED|DER.SEQUENCE); + namePair.add(anotherName); + // DERReader goes back on Constructed things so we need to skip over them + DERValue skip = der.read(); // skip OID + skip = der.read(); // skip Octet String + break; + case EDI_PARTY_NAME: namePair.add(name.getValue()); break; @@ -106,7 +119,9 @@ public class GeneralNames case DIRECTORY_NAME: byte[] b = name.getEncoded(); b[0] = (byte) (DER.CONSTRUCTED|DER.SEQUENCE); - namePair.add(new X500DistinguishedName(b).toString()); + DERReader r = new DERReader (b); + r.read (); + namePair.add(new X500Principal(r.read ().getEncoded ()).toString()); break; case IP_ADDRESS: diff --git a/gnu/javax/crypto/key/BaseKeyAgreementParty.java b/gnu/javax/crypto/key/BaseKeyAgreementParty.java index 5aba47448..bfd9378d2 100644 --- a/gnu/javax/crypto/key/BaseKeyAgreementParty.java +++ b/gnu/javax/crypto/key/BaseKeyAgreementParty.java @@ -40,6 +40,7 @@ package gnu.javax.crypto.key; import gnu.java.security.prng.IRandom; import gnu.java.security.prng.LimitReachedException; +import gnu.java.security.util.PRNG; import java.math.BigInteger; import java.security.SecureRandom; @@ -75,6 +76,9 @@ public abstract class BaseKeyAgreementParty implements IKeyAgreementParty /** The optional {@link IRandom} instance to use. */ protected IRandom irnd = null; + /** Our default source of randomness. */ + private PRNG prng = null; + // Constructor(s) // ------------------------------------------------------------------------- @@ -187,12 +191,18 @@ public abstract class BaseKeyAgreementParty implements IKeyAgreementParty catch (LimitReachedException lre) { irnd = null; - new SecureRandom ().nextBytes(buffer); + getDefaultPRNG().nextBytes(buffer); } } else - { - new SecureRandom ().nextBytes(buffer); - } + getDefaultPRNG().nextBytes(buffer); + } + + private PRNG getDefaultPRNG() + { + if (prng == null) + prng = PRNG.getInstance(); + + return prng; } }
\ No newline at end of file diff --git a/gnu/javax/crypto/key/dh/GnuDHKeyPairGenerator.java b/gnu/javax/crypto/key/dh/GnuDHKeyPairGenerator.java index 5502fa7bf..0d493d3a6 100644 --- a/gnu/javax/crypto/key/dh/GnuDHKeyPairGenerator.java +++ b/gnu/javax/crypto/key/dh/GnuDHKeyPairGenerator.java @@ -41,6 +41,7 @@ package gnu.javax.crypto.key.dh; import gnu.java.security.Registry; import gnu.java.security.hash.Sha160; import gnu.java.security.key.IKeyPairGenerator; +import gnu.java.security.util.PRNG; import java.io.PrintWriter; import java.math.BigInteger; @@ -133,6 +134,9 @@ public class GnuDHKeyPairGenerator implements IKeyPairGenerator private BigInteger g; + /** Our default source of randomness. */ + private PRNG prng = null; + // Constructor(s) // ------------------------------------------------------------------------- @@ -247,8 +251,14 @@ public class GnuDHKeyPairGenerator implements IKeyPairGenerator rnd.nextBytes(buffer); } else - { - new SecureRandom ().nextBytes(buffer); - } + getDefaultPRNG().nextBytes(buffer); + } + + private PRNG getDefaultPRNG() + { + if (prng == null) + prng = PRNG.getInstance(); + + return prng; } } diff --git a/gnu/javax/crypto/key/dh/RFC2631.java b/gnu/javax/crypto/key/dh/RFC2631.java index 656253de1..d6e30b4bc 100644 --- a/gnu/javax/crypto/key/dh/RFC2631.java +++ b/gnu/javax/crypto/key/dh/RFC2631.java @@ -39,6 +39,7 @@ exception statement from your version. */ package gnu.javax.crypto.key.dh; import gnu.java.security.hash.Sha160; +import gnu.java.security.util.PRNG; import gnu.java.security.util.Prime2; import java.math.BigInteger; @@ -87,6 +88,9 @@ public class RFC2631 /** The optional {@link SecureRandom} instance to use. */ private SecureRandom rnd = null; + /** Our default source of randomness. */ + private PRNG prng = null; + // Constructor(s) // ------------------------------------------------------------------------- @@ -238,8 +242,14 @@ public class RFC2631 rnd.nextBytes(buffer); } else - { - new SecureRandom ().nextBytes(buffer); - } + getDefaultPRNG().nextBytes(buffer); + } + + private PRNG getDefaultPRNG() + { + if (prng == null) + prng = PRNG.getInstance(); + + return prng; } } diff --git a/gnu/javax/crypto/key/srp6/SRPKeyPairGenerator.java b/gnu/javax/crypto/key/srp6/SRPKeyPairGenerator.java index 3c8baaa7a..2957fc3c8 100644 --- a/gnu/javax/crypto/key/srp6/SRPKeyPairGenerator.java +++ b/gnu/javax/crypto/key/srp6/SRPKeyPairGenerator.java @@ -40,6 +40,7 @@ package gnu.javax.crypto.key.srp6; import gnu.java.security.Registry; import gnu.java.security.key.IKeyPairGenerator; +import gnu.java.security.util.PRNG; import gnu.java.security.util.Prime2; import java.io.PrintWriter; @@ -126,6 +127,9 @@ public class SRPKeyPairGenerator implements IKeyPairGenerator /** The user's verifier MPI. */ private BigInteger v; + /** Our default source of randomness. */ + private PRNG prng = null; + // Constructor(s) // ------------------------------------------------------------------------- @@ -334,8 +338,14 @@ public class SRPKeyPairGenerator implements IKeyPairGenerator rnd.nextBytes(buffer); } else - { - new SecureRandom ().nextBytes(buffer); - } + getDefaultPRNG().nextBytes(buffer); + } + + private PRNG getDefaultPRNG() + { + if (prng == null) + prng = PRNG.getInstance(); + + return prng; } } diff --git a/gnu/javax/crypto/mac/BaseMac.java b/gnu/javax/crypto/mac/BaseMac.java index e5b47a937..1b42a1644 100644 --- a/gnu/javax/crypto/mac/BaseMac.java +++ b/gnu/javax/crypto/mac/BaseMac.java @@ -130,7 +130,11 @@ public abstract class BaseMac implements IMac public Object clone() throws CloneNotSupportedException { - return super.clone(); + BaseMac result = (BaseMac) super.clone(); + if (this.underlyingHash != null) + result.underlyingHash = (IMessageDigest) this.underlyingHash.clone(); + + return result; } // methods to be implemented by concrete subclasses ------------------------ diff --git a/gnu/javax/crypto/mac/HMac.java b/gnu/javax/crypto/mac/HMac.java index 4e664160a..c1f97b541 100644 --- a/gnu/javax/crypto/mac/HMac.java +++ b/gnu/javax/crypto/mac/HMac.java @@ -132,12 +132,15 @@ public class HMac extends BaseMac implements Cloneable // java.lang.Cloneable interface implementation ---------------------------- - public Object clone() + public Object clone() throws CloneNotSupportedException { - HMac result = new HMac((IMessageDigest) underlyingHash.clone()); - result.ipadHash = this.ipadHash; - result.opadHash = this.opadHash; - result.ipad = this.ipad.clone(); + HMac result = (HMac) super.clone(); + if (this.ipadHash != null) + result.ipadHash = (IMessageDigest) this.ipadHash.clone(); + if (this.opadHash != null) + result.opadHash = (IMessageDigest) this.opadHash.clone(); + if (this.ipad != null) + result.ipad = (byte[]) this.ipad.clone(); return result; } @@ -322,4 +325,4 @@ public class HMac extends BaseMac implements Cloneable } return valid.booleanValue(); } -}
\ No newline at end of file +} diff --git a/gnu/javax/crypto/pad/PKCS1_V1_5.java b/gnu/javax/crypto/pad/PKCS1_V1_5.java index 5b4f7b850..03c3d61a3 100644 --- a/gnu/javax/crypto/pad/PKCS1_V1_5.java +++ b/gnu/javax/crypto/pad/PKCS1_V1_5.java @@ -40,10 +40,10 @@ package gnu.javax.crypto.pad; import gnu.java.security.Registry; import gnu.java.security.sig.rsa.EME_PKCS1_V1_5; +import gnu.java.security.util.PRNG; import gnu.java.security.util.Util; import java.io.PrintWriter; -import java.util.Random; /** * <p>A padding algorithm implementation of the EME-PKCS1-V1.5 encoding/decoding @@ -143,7 +143,7 @@ public class PKCS1_V1_5 extends BasePad { final int[] mLen = new int[] { 16, 20, 32, 48, 64 }; final byte[] M = new byte[mLen[mLen.length - 1]]; - new Random ().nextBytes(M); + PRNG.getInstance().nextBytes(M); final byte[] EM = new byte[1024]; byte[] p; int bs, i, j; diff --git a/gnu/javax/crypto/sasl/srp/KDF.java b/gnu/javax/crypto/sasl/srp/KDF.java index 161ec78b6..0d5eeacd1 100644 --- a/gnu/javax/crypto/sasl/srp/KDF.java +++ b/gnu/javax/crypto/sasl/srp/KDF.java @@ -40,11 +40,11 @@ package gnu.javax.crypto.sasl.srp; import gnu.java.security.Registry; import gnu.java.security.prng.LimitReachedException; +import gnu.java.security.util.PRNG; import gnu.javax.crypto.cipher.IBlockCipher; import gnu.javax.crypto.prng.UMacGenerator; import java.util.HashMap; -import java.util.Random; /** * <p>The SASL-SRP KDF implementation, which is also used, depending on how it @@ -60,6 +60,11 @@ public class KDF private static final int AES_KEY_SIZE = 16; // default key size for the AES + private static final byte[] buffer = new byte[1]; + + /** Our default source of randomness. */ + private static final PRNG prng = PRNG.getInstance(); + /** The shared secret K to use. */ // private byte[] keyMaterial; /** The underlying UMAC Generator instance. */ @@ -117,11 +122,18 @@ public class KDF else { keyMaterial = new byte[AES_BLOCK_SIZE]; - ndx = new Random ().nextInt (256); // XXX does this need to be secure? + while (ndx < 1 || ndx > 255) + ndx = (byte) nextByte(); } return new KDF(keyMaterial, ndx); } + private static synchronized final int nextByte() + { + prng.nextBytes(buffer); + return (buffer[0] & 0xFF); + } + // Instance methods // ------------------------------------------------------------------------- diff --git a/gnu/javax/crypto/sasl/srp/SRPClient.java b/gnu/javax/crypto/sasl/srp/SRPClient.java index 26a9e8aaf..1a1664ff7 100644 --- a/gnu/javax/crypto/sasl/srp/SRPClient.java +++ b/gnu/javax/crypto/sasl/srp/SRPClient.java @@ -40,6 +40,7 @@ package gnu.javax.crypto.sasl.srp; import gnu.java.security.Registry; import gnu.java.security.hash.MD5; +import gnu.java.security.util.PRNG; import gnu.java.security.util.Util; import gnu.javax.crypto.key.IKeyAgreementParty; @@ -65,7 +66,6 @@ import java.io.ByteArrayOutputStream; import java.io.UnsupportedEncodingException; import java.math.BigInteger; import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; import java.util.Arrays; import java.util.HashMap; import java.util.StringTokenizer; @@ -160,6 +160,9 @@ public class SRPClient extends ClientMechanism implements SaslClient private IKeyAgreementParty clientHandler = KeyAgreementFactory.getPartyAInstance(Registry.SRP_SASL_KA); + /** Our default source of randomness. */ + private PRNG prng = null; + // Constructor(s) // ------------------------------------------------------------------------- @@ -563,7 +566,8 @@ public class SRPClient extends ClientMechanism implements SaslClient // if session re-use generate new 16-byte nonce if (sid.length != 0) { - cn = new SecureRandom ().generateSeed (16); + cn = new byte[16]; + getDefaultPRNG().nextBytes(cn); } else { @@ -1091,7 +1095,7 @@ public class SRPClient extends ClientMechanism implements SaslClient final int blockSize = cipher.defaultBlockSize(); // 3. generate random iv cIV = new byte[blockSize]; - new SecureRandom ().nextBytes(cIV); + getDefaultPRNG().nextBytes(cIV); } srp = SRP.instance(mdName); @@ -1196,4 +1200,12 @@ public class SRPClient extends ClientMechanism implements SaslClient outCipher)); } } + + private PRNG getDefaultPRNG() + { + if (prng == null) + prng = PRNG.getInstance(); + + return prng; + } }
\ No newline at end of file diff --git a/gnu/javax/crypto/sasl/srp/SRPServer.java b/gnu/javax/crypto/sasl/srp/SRPServer.java index 421da6e1b..25405f106 100644 --- a/gnu/javax/crypto/sasl/srp/SRPServer.java +++ b/gnu/javax/crypto/sasl/srp/SRPServer.java @@ -39,6 +39,7 @@ exception statement from your version. */ package gnu.javax.crypto.sasl.srp; import gnu.java.security.Registry; +import gnu.java.security.util.PRNG; import gnu.java.security.util.Util; import gnu.javax.crypto.assembly.Direction; @@ -61,7 +62,6 @@ import java.io.PrintWriter; import java.io.ByteArrayOutputStream; import java.io.UnsupportedEncodingException; import java.math.BigInteger; -import java.security.SecureRandom; import java.util.Arrays; import java.util.HashMap; import java.util.StringTokenizer; @@ -73,7 +73,7 @@ import javax.security.sasl.SaslServer; /** * <p>The SASL-SRP server-side mechanism.</p> * - * @version $Revision: 1.1 $ + * @version $Revision: 1.1.2.1 $ */ public class SRPServer extends ServerMechanism implements SaslServer { @@ -148,6 +148,9 @@ public class SRPServer extends ServerMechanism implements SaslServer private IKeyAgreementParty serverHandler = KeyAgreementFactory.getPartyBInstance(Registry.SRP_SASL_KA); + /** Our default source of randomness. */ + private PRNG prng = null; + // Constructor(s) // ------------------------------------------------------------------------- @@ -593,7 +596,7 @@ public class SRPServer extends ServerMechanism implements SaslServer { sn = new byte[16]; } - new SecureRandom ().nextBytes(sn); + getDefaultPRNG().nextBytes(sn); setupSecurityServices(false); @@ -1072,9 +1075,7 @@ public class SRPServer extends ServerMechanism implements SaslServer sIV = new byte[blockSize]; if (blockSize > 0) - { - new SecureRandom ().nextBytes(sIV); - } + getDefaultPRNG().nextBytes(sIV); } private void setupSecurityServices(final boolean newSession) @@ -1144,4 +1145,12 @@ public class SRPServer extends ServerMechanism implements SaslServer outCipher)); } } + + private PRNG getDefaultPRNG() + { + if (prng == null) + prng = PRNG.getInstance(); + + return prng; + } }
\ No newline at end of file diff --git a/gnu/regexp/RE.java b/gnu/regexp/RE.java index ad2630e68..bda977999 100644 --- a/gnu/regexp/RE.java +++ b/gnu/regexp/RE.java @@ -217,6 +217,13 @@ public class RE extends REToken { */ public static final int REG_NO_INTERPOLATE = 128; + /** + * Execution flag. + * Try to match the whole input string. An implicit match-end operator + * is added to this regexp. + */ + public static final int REG_TRY_ENTIRE_MATCH = 256; + /** Returns a string representing the version of the gnu.regexp package. */ public static final String version() { return VERSION; @@ -452,6 +459,7 @@ public class RE extends REToken { // FIXME: asciiEsc == 0 means asciiEsc is not set. But what if // \u0000 is used as a meaningful character? char asciiEsc = 0; + NamedProperty np = null; if (("dswDSW".indexOf(pattern[index]) != -1) && syntax.get(RESyntax.RE_CHAR_CLASS_ESC_IN_LISTS)) { switch (pattern[index]) { case 'D': @@ -471,6 +479,12 @@ public class RE extends REToken { break; } } + if (("pP".indexOf(pattern[index]) != -1) && syntax.get(RESyntax.RE_NAMED_PROPERTY)) { + np = getNamedProperty(pattern, index - 1, pLength); + if (np == null) + throw new REException("invalid escape sequence", REException.REG_ESCAPE, index); + index = index - 1 + np.len - 1; + } else { CharExpression ce = getCharExpression(pattern, index - 1, pLength, syntax); if (ce == null) @@ -482,6 +496,8 @@ public class RE extends REToken { if (posixID != -1) { options.addElement(new RETokenPOSIX(subIndex,posixID,insens,negate)); + } else if (np != null) { + options.addElement(getRETokenNamedProperty(subIndex,np,insens,index)); } else if (asciiEsc != 0) { lastChar = asciiEsc; } else { @@ -984,6 +1000,19 @@ public class RE extends REToken { currentToken = new RETokenChar(subIndex,ce.ch,insens); } + // NAMED PROPERTY + // \p{prop}, \P{prop} + + else if ((unit.bk && (unit.ch == 'p') && syntax.get(RESyntax.RE_NAMED_PROPERTY)) || + (unit.bk && (unit.ch == 'P') && syntax.get(RESyntax.RE_NAMED_PROPERTY))) { + NamedProperty np = getNamedProperty(pattern, index - 2, pLength); + if (np == null) + throw new REException("invalid escape sequence", REException.REG_ESCAPE, index); + index = index - 2 + np.len; + addToken(currentToken); + currentToken = getRETokenNamedProperty(subIndex,np,insens,index); + } + // NON-SPECIAL CHARACTER (or escape to make literal) // c | \* for example @@ -1119,6 +1148,76 @@ public class RE extends REToken { } /** + * This class represents a substring in a pattern string expressing + * a named property. + * "\pA" : Property named "A" + * "\p{prop}" : Property named "prop" + * "\PA" : Property named "A" (Negated) + * "\P{prop}" : Property named "prop" (Negated) + */ + private static class NamedProperty { + /** Property name */ + String name; + /** Negated or not */ + boolean negate; + /** length of this expression */ + int len; + } + + private NamedProperty getNamedProperty(char[] input, int pos, int lim) { + NamedProperty np = new NamedProperty(); + char c = input[pos]; + if (c == '\\') { + if (++pos >= lim) return null; + c = input[pos++]; + switch(c) { + case 'p': + np.negate = false; + break; + case 'P': + np.negate = true; + break; + default: + return null; + } + c = input[pos++]; + if (c == '{') { + int p = -1; + for (int i = pos; i < lim; i++) { + if (input[i] == '}') { + p = i; + break; + } + } + if (p < 0) return null; + int len = p - pos; + np.name = new String(input, pos, len); + np.len = len + 4; + } + else { + np.name = new String(input, pos - 1, 1); + np.len = 3; + } + return np; + } + else return null; + } + + private static RETokenNamedProperty getRETokenNamedProperty( + int subIndex, NamedProperty np, boolean insens, int index) + throws REException { + try { + return new RETokenNamedProperty(subIndex, np.name, insens, np.negate); + } + catch (REException e) { + REException ree; + ree = new REException(e.getMessage(), REException.REG_ESCAPE, index); + ree.initCause(e); + throw ree; + } + } + + /** * Checks if the regular expression matches the input in its entirety. * * @param input The input text. @@ -1265,20 +1364,14 @@ public class RE extends REToken { /* Implements abstract method REToken.match() */ boolean match(CharIndexed input, REMatch mymatch) { - int origin = mymatch.index; - boolean b; if (firstToken == null) { - b = next(input, mymatch); - if (b) mymatch.empty = (mymatch.index == origin); - return b; + return next(input, mymatch); } // Note the start of this subexpression mymatch.start[subIndex] = mymatch.index; - b = firstToken.match(input, mymatch); - if (b) mymatch.empty = (mymatch.index == origin); - return b; + return firstToken.match(input, mymatch); } /** @@ -1337,23 +1430,34 @@ public class RE extends REToken { } REMatch getMatchImpl(CharIndexed input, int anchor, int eflags, StringBuffer buffer) { + boolean tryEntireMatch = ((eflags & REG_TRY_ENTIRE_MATCH) != 0); + RE re = (tryEntireMatch ? (RE) this.clone() : this); + if (tryEntireMatch) { + re.chain(new RETokenEnd(0, null)); + } // Create a new REMatch to hold results REMatch mymatch = new REMatch(numSubs, anchor, eflags); do { // Optimization: check if anchor + minimumLength > length if (minimumLength == 0 || input.charAt(minimumLength-1) != CharIndexed.OUT_OF_BOUNDS) { - if (match(input, mymatch)) { - // Find longest match of them all to observe leftmost longest - REMatch longest = mymatch; + if (re.match(input, mymatch)) { + REMatch best = mymatch; + // We assume that the match that coms first is the best. + // And the following "The longer, the better" rule has + // been commented out. The longest is not neccesarily + // the best. For example, "a" out of "aaa" is the best + // match for /a+?/. + /* + // Find best match of them all to observe leftmost longest while ((mymatch = mymatch.next) != null) { - if (mymatch.index > longest.index) { - longest = mymatch; + if (mymatch.index > best.index) { + best = mymatch; } } - - longest.end[0] = longest.index; - longest.finish(input); - return longest; + */ + best.end[0] = best.index; + best.finish(input); + return best; } } mymatch.clear(++anchor); diff --git a/gnu/regexp/REMatch.java b/gnu/regexp/REMatch.java index e06ae36cf..ce428d48c 100644 --- a/gnu/regexp/REMatch.java +++ b/gnu/regexp/REMatch.java @@ -67,7 +67,8 @@ public final class REMatch implements Serializable, Cloneable { int[] start; // start positions (relative to offset) for each (sub)exp. int[] end; // end positions for the same REMatch next; // other possibility (to avoid having to use arrays) - boolean empty; // empty string matched + boolean empty; // empty string matched. This flag is used only within + // RETokenRepeated. public Object clone() { try { @@ -89,7 +90,6 @@ public final class REMatch implements Serializable, Cloneable { index = other.index; // need to deep clone? next = other.next; - empty = other.empty; } REMatch(int subs, int anchor, int eflags) { @@ -126,7 +126,6 @@ public final class REMatch implements Serializable, Cloneable { start[i] = end[i] = -1; } next = null; // cut off alternates - empty = false; } /** @@ -180,7 +179,9 @@ public final class REMatch implements Serializable, Cloneable { * @param sub Index of the subexpression. */ public String toString(int sub) { - if ((sub >= start.length) || (start[sub] == -1)) return ""; + if ((sub >= start.length) || sub < 0) + throw new IndexOutOfBoundsException("No group " + sub); + if (start[sub] == -1) return null; return (matchedText.substring(start[sub],end[sub])); } @@ -263,4 +264,42 @@ public final class REMatch implements Serializable, Cloneable { if (pos < input.length()) output.append(input.charAt(pos)); return output.toString(); } + + static class REMatchList { + REMatch head; + REMatch tail; + REMatchList() { + head = tail = null; + } + /* Not used now. But we may need this some day? + void addHead(REMatch newone) { + if (head == null) { + head = newone; + tail = newone; + while (tail.next != null) { + tail = tail.next; + } + } + else { + REMatch tmp = newone; + while (tmp.next != null) tmp = tmp.next; + tmp.next = head; + head = newone; + } + } + */ + void addTail(REMatch newone) { + if (head == null) { + head = newone; + tail = newone; + } + else { + tail.next = newone; + } + while (tail.next != null) { + tail = tail.next; + } + } + } + } diff --git a/gnu/regexp/RESyntax.java b/gnu/regexp/RESyntax.java index 269179364..73f4a0aaa 100644 --- a/gnu/regexp/RESyntax.java +++ b/gnu/regexp/RESyntax.java @@ -222,7 +222,12 @@ public final class RESyntax implements Serializable { */ public static final int RE_UNICODE_CHAR = 29; - private static final int BIT_TOTAL = 30; + /** + * Syntax bit. Allow named property (\p{P}, \P{p}), as in Perl5. + */ + public static final int RE_NAMED_PROPERTY = 30; + + private static final int BIT_TOTAL = 31; /** * Predefined syntax. @@ -445,6 +450,7 @@ public final class RESyntax implements Serializable { .set(RE_EMBEDDED_FLAGS) // (?imsx-imsx) .set(RE_OCTAL_CHAR) // \0377 .set(RE_HEX_CHAR) // \x1b + .set(RE_NAMED_PROPERTY) // \p{prop}, \P{prop} .makeFinal(); RE_SYNTAX_PERL5_S = new RESyntax(RE_SYNTAX_PERL5) diff --git a/gnu/regexp/RETokenBackRef.java b/gnu/regexp/RETokenBackRef.java index 3414ecf97..060a6cf7d 100644 --- a/gnu/regexp/RETokenBackRef.java +++ b/gnu/regexp/RETokenBackRef.java @@ -57,7 +57,6 @@ final class RETokenBackRef extends REToken { b = mymatch.start[num]; e = mymatch.end[num]; if ((b==-1)||(e==-1)) return false; // this shouldn't happen, but... - int origin = mymatch.index; for (int i=b; i<e; i++) { char c1 = input.charAt(mymatch.index+i-b); char c2 = input.charAt(i); @@ -74,9 +73,7 @@ final class RETokenBackRef extends REToken { } } mymatch.index += e-b; - boolean result = next(input, mymatch); - if (result) mymatch.empty = (mymatch.index == origin); - return result; + return next(input, mymatch); } void dump(StringBuffer os) { diff --git a/gnu/regexp/RETokenNamedProperty.java b/gnu/regexp/RETokenNamedProperty.java new file mode 100644 index 000000000..c4668d7fb --- /dev/null +++ b/gnu/regexp/RETokenNamedProperty.java @@ -0,0 +1,297 @@ +/* gnu/regexp/RETokenNamedProperty.java + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.regexp; + +final class RETokenNamedProperty extends REToken { + String name; + boolean insens; + boolean negate; + Handler handler; + + // Grouped properties + static final byte[] LETTER = new byte[] + { Character.LOWERCASE_LETTER, + Character.UPPERCASE_LETTER, + Character.TITLECASE_LETTER, + Character.MODIFIER_LETTER, + Character.OTHER_LETTER }; + + static final byte[] MARK = new byte[] + { Character.NON_SPACING_MARK, + Character.COMBINING_SPACING_MARK, + Character.ENCLOSING_MARK }; + + static final byte[] SEPARATOR = new byte[] + { Character.SPACE_SEPARATOR, + Character.LINE_SEPARATOR, + Character.PARAGRAPH_SEPARATOR }; + + static final byte[] SYMBOL = new byte[] + { Character.MATH_SYMBOL, + Character.CURRENCY_SYMBOL, + Character.MODIFIER_SYMBOL, + Character.OTHER_SYMBOL }; + + static final byte[] NUMBER = new byte[] + { Character.DECIMAL_DIGIT_NUMBER, + Character.LETTER_NUMBER, + Character.OTHER_NUMBER }; + + static final byte[] PUNCTUATION = new byte[] + { Character.DASH_PUNCTUATION, + Character.START_PUNCTUATION, + Character.END_PUNCTUATION, + Character.CONNECTOR_PUNCTUATION, + Character.OTHER_PUNCTUATION, + Character.INITIAL_QUOTE_PUNCTUATION, + Character.FINAL_QUOTE_PUNCTUATION}; + + static final byte[] OTHER = new byte[] + { Character.CONTROL, + Character.FORMAT, + Character.PRIVATE_USE, + Character.SURROGATE, + Character.UNASSIGNED }; + + RETokenNamedProperty(int subIndex, String name, boolean insens, boolean negate) throws REException { + super(subIndex); + this.name = name; + this.insens = insens; + this.negate = negate; + handler = getHandler(name); + } + + int getMinimumLength() { + return 1; + } + + boolean match(CharIndexed input, REMatch mymatch) { + char ch = input.charAt(mymatch.index); + if (ch == CharIndexed.OUT_OF_BOUNDS) + return false; + + boolean retval = handler.includes(ch); + if (insens) { + retval = retval || + handler.includes(Character.toUpperCase(ch)) || + handler.includes(Character.toLowerCase(ch)); + } + + if (negate) retval = !retval; + if (retval) { + ++mymatch.index; + return next(input, mymatch); + } + else return false; + } + + void dump(StringBuffer os) { + os.append("\\") + .append(negate ? "P" : "p") + .append("{" + name + "}"); + } + + private abstract static class Handler { + public abstract boolean includes(char c); + } + + private Handler getHandler(String name) throws REException { + if (name.equals("Lower") || + name.equals("Upper") || + // name.equals("ASCII") || + name.equals("Alpha") || + name.equals("Digit") || + name.equals("Alnum") || + name.equals("Punct") || + name.equals("Graph") || + name.equals("Print") || + name.equals("Blank") || + name.equals("Cntrl") || + name.equals("XDigit") || + name.equals("Space") ) { + return new POSIXHandler(name); + } + if (name.startsWith("In")) { + try { + name = name.substring(2); + Character.UnicodeBlock block = Character.UnicodeBlock.forName(name); + return new UnicodeBlockHandler(block); + } + catch (IllegalArgumentException e) { + throw new REException("Invalid Unicode block name: " + name, REException.REG_ESCAPE, 0); + } + } + if (name.startsWith("Is")) { + name = name.substring(2); + } + + // "grouped properties" + if (name.equals("L")) + return new UnicodeCategoriesHandler(LETTER); + if (name.equals("M")) + return new UnicodeCategoriesHandler(MARK); + if (name.equals("Z")) + return new UnicodeCategoriesHandler(SEPARATOR); + if (name.equals("S")) + return new UnicodeCategoriesHandler(SYMBOL); + if (name.equals("N")) + return new UnicodeCategoriesHandler(NUMBER); + if (name.equals("P")) + return new UnicodeCategoriesHandler(PUNCTUATION); + if (name.equals("C")) + return new UnicodeCategoriesHandler(OTHER); + + if (name.equals("Mc")) + return new UnicodeCategoryHandler(Character.COMBINING_SPACING_MARK); + if (name.equals("Pc")) + return new UnicodeCategoryHandler(Character.CONNECTOR_PUNCTUATION); + if (name.equals("Cc")) + return new UnicodeCategoryHandler(Character.CONTROL); + if (name.equals("Sc")) + return new UnicodeCategoryHandler(Character.CURRENCY_SYMBOL); + if (name.equals("Pd")) + return new UnicodeCategoryHandler(Character.DASH_PUNCTUATION); + if (name.equals("Nd")) + return new UnicodeCategoryHandler(Character.DECIMAL_DIGIT_NUMBER); + if (name.equals("Me")) + return new UnicodeCategoryHandler(Character.ENCLOSING_MARK); + if (name.equals("Pe")) + return new UnicodeCategoryHandler(Character.END_PUNCTUATION); + if (name.equals("Pf")) + return new UnicodeCategoryHandler(Character.FINAL_QUOTE_PUNCTUATION); + if (name.equals("Cf")) + return new UnicodeCategoryHandler(Character.FORMAT); + if (name.equals("Pi")) + return new UnicodeCategoryHandler(Character.INITIAL_QUOTE_PUNCTUATION); + if (name.equals("Nl")) + return new UnicodeCategoryHandler(Character.LETTER_NUMBER); + if (name.equals("Zl")) + return new UnicodeCategoryHandler(Character.LINE_SEPARATOR); + if (name.equals("Ll")) + return new UnicodeCategoryHandler(Character.LOWERCASE_LETTER); + if (name.equals("Sm")) + return new UnicodeCategoryHandler(Character.MATH_SYMBOL); + if (name.equals("Lm")) + return new UnicodeCategoryHandler(Character.MODIFIER_LETTER); + if (name.equals("Sk")) + return new UnicodeCategoryHandler(Character.MODIFIER_SYMBOL); + if (name.equals("Mn")) + return new UnicodeCategoryHandler(Character.NON_SPACING_MARK); + if (name.equals("Lo")) + return new UnicodeCategoryHandler(Character.OTHER_LETTER); + if (name.equals("No")) + return new UnicodeCategoryHandler(Character.OTHER_NUMBER); + if (name.equals("Po")) + return new UnicodeCategoryHandler(Character.OTHER_PUNCTUATION); + if (name.equals("So")) + return new UnicodeCategoryHandler(Character.OTHER_SYMBOL); + if (name.equals("Zp")) + return new UnicodeCategoryHandler(Character.PARAGRAPH_SEPARATOR); + if (name.equals("Co")) + return new UnicodeCategoryHandler(Character.PRIVATE_USE); + if (name.equals("Zs")) + return new UnicodeCategoryHandler(Character.SPACE_SEPARATOR); + if (name.equals("Ps")) + return new UnicodeCategoryHandler(Character.START_PUNCTUATION); + if (name.equals("Cs")) + return new UnicodeCategoryHandler(Character.SURROGATE); + if (name.equals("Lt")) + return new UnicodeCategoryHandler(Character.TITLECASE_LETTER); + if (name.equals("Cn")) + return new UnicodeCategoryHandler(Character.UNASSIGNED); + if (name.equals("Lu")) + return new UnicodeCategoryHandler(Character.UPPERCASE_LETTER); + throw new REException("unsupported name " + name, REException.REG_ESCAPE, 0); + } + + private static class POSIXHandler extends Handler { + private RETokenPOSIX retoken; + private REMatch mymatch = new REMatch(0,0,0); + private char[] chars = new char[1]; + private CharIndexedCharArray ca = new CharIndexedCharArray(chars, 0); + public POSIXHandler(String name) { + int posixId = RETokenPOSIX.intValue(name.toLowerCase()); + if (posixId != -1) + retoken = new RETokenPOSIX(0,posixId,false,false); + else + throw new RuntimeException("Unknown posix ID: " + name); + } + public boolean includes(char c) { + chars[0] = c; + mymatch.index = 0; + return retoken.match(ca, mymatch); + } + } + + private static class UnicodeCategoryHandler extends Handler { + public UnicodeCategoryHandler(byte category) { + this.category = (int)category; + } + private int category; + public boolean includes(char c) { + return Character.getType(c) == category; + } + } + + private static class UnicodeCategoriesHandler extends Handler { + public UnicodeCategoriesHandler(byte[] categories) { + this.categories = categories; + } + private byte[] categories; + public boolean includes(char c) { + int category = Character.getType(c); + for (int i = 0; i < categories.length; i++) + if (category == categories[i]) + return true; + return false; + } + } + + private static class UnicodeBlockHandler extends Handler { + public UnicodeBlockHandler(Character.UnicodeBlock block) { + this.block = block; + } + private Character.UnicodeBlock block; + public boolean includes(char c) { + Character.UnicodeBlock cblock = Character.UnicodeBlock.of(c); + return (cblock != null && cblock.equals(block)); + } + } + +} diff --git a/gnu/regexp/RETokenOneOf.java b/gnu/regexp/RETokenOneOf.java index 426330443..d5902c6f0 100644 --- a/gnu/regexp/RETokenOneOf.java +++ b/gnu/regexp/RETokenOneOf.java @@ -94,11 +94,10 @@ final class RETokenOneOf extends REToken { } private boolean matchP(CharIndexed input, REMatch mymatch) { - REMatch newMatch = null; - REMatch last = null; + REMatch.REMatchList newMatch = new REMatch.REMatchList(); REToken tk; for (int i=0; i < options.size(); i++) { - // In ordaer that the backtracking can work, + // In order that the backtracking can work, // each option must be chained to the next token. // But the chain method has some side effect, so // we use clones. @@ -108,21 +107,15 @@ final class RETokenOneOf extends REToken { tk.subIndex = this.subIndex; REMatch tryMatch = (REMatch) mymatch.clone(); if (tk.match(input, tryMatch)) { // match was successful - if (last == null) { - newMatch = tryMatch; - last = tryMatch; - } else { - last.next = tryMatch; - last = tryMatch; - } + newMatch.addTail(tryMatch); } // is a match } // try next option - if (newMatch != null) { + if (newMatch.head != null) { // set contents of mymatch equal to newMatch // try each one that matched - mymatch.assignFrom(newMatch); + mymatch.assignFrom(newMatch.head); return true; } else { return false; diff --git a/gnu/regexp/RETokenRepeated.java b/gnu/regexp/RETokenRepeated.java index 167ca9991..100b2c066 100644 --- a/gnu/regexp/RETokenRepeated.java +++ b/gnu/regexp/RETokenRepeated.java @@ -84,6 +84,29 @@ final class RETokenRepeated extends REToken { return (min * token.getMinimumLength()); } + boolean stopMatchingIfSatisfied = true; + + private static REMatch findDoables(REToken tk, + CharIndexed input, REMatch mymatch) { + + REMatch.REMatchList doables = new REMatch.REMatchList(); + + // try next repeat at all possible positions + for (REMatch current = mymatch; + current != null; current = current.next) { + REMatch recurrent = (REMatch) current.clone(); + int origin = recurrent.index; + tk = (REToken) tk.clone(); + tk.next = tk.uncle = null; + if (tk.match(input, recurrent)) { + if (recurrent.index == origin) recurrent.empty = true; + // add all items in current to doables array + doables.addTail(recurrent); + } + } + return doables.head; + } + // We do need to save every possible point, but the number of clone() // invocations here is really a killer for performance on non-stingy // repeat operators. I'm open to suggestions... @@ -93,65 +116,34 @@ final class RETokenRepeated extends REToken { // the subexpression back-reference operator allow that? boolean match(CharIndexed input, REMatch mymatch) { - int origin = mymatch.index; - // number of times we've matched so far - int numRepeats = 0; - // Possible positions for the next repeat to match at REMatch newMatch = mymatch; - REMatch last = null; - REMatch current; - // Add the '0-repeats' index - // positions.elementAt(z) == position [] in input after <<z>> matches - Vector positions = new Vector(); - positions.addElement(newMatch); + // {0} needs some special treatment. + if (alwaysEmpty) { + REMatch result = matchRest(input, newMatch); + if (result != null) { + mymatch.assignFrom(result); + return true; + } + else { + return false; + } + } + + // number of times we've matched so far + int numRepeats = 0; - // Declare variables used in loop REMatch doables; - REMatch doablesLast; - REMatch recurrent; int lastIndex = mymatch.index; + boolean emptyMatchFound = false; - do { - // Check for stingy match for each possibility. - if ((stingy && (numRepeats >= min)) || alwaysEmpty) { - REMatch result = matchRest(input, newMatch); - if (result != null) { - mymatch.assignFrom(result); - mymatch.empty = (mymatch.index == origin); - return true; - } - else { - // Special case of {0}. It must always match an empty string. - if (alwaysEmpty) return false; - } - } - - doables = null; - doablesLast = null; + while (numRepeats < min) { + doables = findDoables(token, input, newMatch); - // try next repeat at all possible positions - for (current = newMatch; current != null; current = current.next) { - recurrent = (REMatch) current.clone(); - if (token.match(input, recurrent)) { - // add all items in current to doables array - if (doables == null) { - doables = recurrent; - doablesLast = recurrent; - } else { - // Order these from longest to shortest - // Start by assuming longest (more repeats) - doablesLast.next = recurrent; - } - // Find new doablesLast - while (doablesLast.next != null) { - doablesLast = doablesLast.next; - } - } - } - // if none of the possibilities worked out, break out of do/while - if (doables == null) break; + // if none of the possibilities worked out, + // it means that minimum number of repeats could not be found. + if (doables == null) return false; // reassign where the next repeat can match newMatch = doables; @@ -159,94 +151,92 @@ final class RETokenRepeated extends REToken { // increment how many repeats we've successfully found ++numRepeats; - positions.addElement(newMatch); + if (newMatch.empty) { + numRepeats = min; + emptyMatchFound = true; + break; + } + lastIndex = newMatch.index; + } + + Vector positions = new Vector(); + + while (numRepeats <= max) { + // We want to check something like + // if (stingy) + // and neglect the further matching. But experience tells + // such neglection may cause incomplete matching. + // For example, if we neglect the seemingly unnecessay + // matching, /^(b+?|a){1,2}?c/ cannot match "bbc". + // On the other hand, if we do not stop the unnecessary + // matching, /(([a-c])b*?\2)*/ matches "ababbbcbc" + // entirely when we wan to find only "ababb". + // In order to make regression tests pass, we do as we did. + if (stopMatchingIfSatisfied && stingy) { + REMatch results = matchRest(input, newMatch); + if (results != null) { + mymatch.assignFrom(results); + return true; + } + } + positions.add(newMatch); + if (emptyMatchFound) break; + + doables = findDoables(token, input, newMatch); + if (doables == null) break; // doables.index == lastIndex occurs either // (1) when an empty string was the longest // that matched this token. - // And this case occurs either - // (1-1) when this token is always empty, - // for example "()" or "(())". - // (1-2) when this token is not always empty - // but can match an empty string, for example, - // "a*", "(a|)". // or // (2) when the same string matches this token many times. // For example, "acbab" itself matches "a.*b" and // its substrings "acb" and "ab" also match. + // In this case, we do not have to go further until + // numRepeats == max because the more numRepeats grows, + // the shorter the substring matching this token becomes. + // So the previous succesful match must have bee the best + // match. But this is not necessarily the case if stingy. if (doables.index == lastIndex) { if (doables.empty) { - // Case (1): We break here, otherwise we will fall - // into an endless loop. - if (numRepeats < min) numRepeats = min; - break; - } + emptyMatchFound = true; + } else { - // Case (2): We cannot break here because, for example, - // "acbacb" matches "a.*b" up to 2 times but - // not 3 times. So we have to check numRepeats >= min. - // But we do not have to go further until numRepeats == max - // because the more numRepeats grows, the shorter the - // substring matching this token becomes. - if (numRepeats > min) { - // This means the previous match was successful, - // and that must be the best match. This match - // resulted in shortening the matched substring. - numRepeats--; - positions.remove(positions.size() - 1); - break; - } - if (numRepeats == min) break; + if (!stingy) break; } - } - lastIndex = doables.index; - } while (numRepeats < max); - - // If there aren't enough repeats, then fail - if (numRepeats < min) return false; - - // We're greedy, but ease off until a true match is found - int posIndex = positions.size(); - + } + numRepeats++; + newMatch = doables; + lastIndex = newMatch.index; + } + + // We're greedy, but ease off until a true match is found. // At this point we've either got too many or just the right amount. // See if this numRepeats works with the rest of the regexp. - REMatch allResults = null; - REMatch allResultsLast = null; - - REMatch results = null; - int indexCount = posIndex - min; - if (indexCount <= 0) { - // This case occurs when we exited the previous do loop before - // numRepeats >= min because an empty string matched the token. - // In this case, an empty string can match as many times as - // desired. - indexCount = 1; - } - while (indexCount-- > 0) { - --posIndex; - newMatch = (REMatch) positions.elementAt(posIndex); - results = matchRest(input, newMatch); - if (results != null) { - if (allResults == null) { - allResults = results; - allResultsLast = results; - } else { - // Order these from longest to shortest - // Start by assuming longest (more repeats) - allResultsLast.next = results; - } - // Find new doablesLast - while (allResultsLast.next != null) { - allResultsLast = allResultsLast.next; - } + + REMatch.REMatchList allResults = new REMatch.REMatchList(); + + int posCount = positions.size(); + int posIndex = (stingy ? 0 : posCount - 1); + + while (posCount-- > 0) { + REMatch m = (REMatch) positions.elementAt(posIndex); + if (stingy) posIndex++; else posIndex--; + + REMatch results = matchRest(input, m); + if (results != null) { + // Order these from longest to shortest + // Start by assuming longest (more repeats) + // If stingy the order is shortest to longest. + allResults.addTail(results); + } + else { + if (possessive) break; } - // else did not match rest of the tokens, try again on smaller sample - // or break out when performing possessive matching - if (possessive) break; } - if (allResults != null) { - mymatch.assignFrom(allResults); // does this get all? - mymatch.empty = (mymatch.index == origin); + + if (allResults.head != null) { + mymatch.assignFrom(allResults.head); // does this get all? return true; } // If we fall out, no matches. @@ -255,27 +245,17 @@ final class RETokenRepeated extends REToken { private REMatch matchRest(CharIndexed input, final REMatch newMatch) { REMatch current, single; - REMatch doneIndex = null; - REMatch doneIndexLast = null; + REMatch.REMatchList doneIndex = new REMatch.REMatchList(); // Test all possible matches for this number of repeats for (current = newMatch; current != null; current = current.next) { // clone() separates a single match from the chain single = (REMatch) current.clone(); if (next(input, single)) { // chain results to doneIndex - if (doneIndex == null) { - doneIndex = single; - doneIndexLast = single; - } else { - doneIndexLast.next = single; - } - // Find new doneIndexLast - while (doneIndexLast.next != null) { - doneIndexLast = doneIndexLast.next; - } + doneIndex.addTail(single); } } - return doneIndex; + return doneIndex.head; } void dump(StringBuffer os) { diff --git a/gnu/xml/stream/SAXParser.java b/gnu/xml/stream/SAXParser.java index 4c0edb975..46fe4abe2 100644 --- a/gnu/xml/stream/SAXParser.java +++ b/gnu/xml/stream/SAXParser.java @@ -904,7 +904,16 @@ public class SAXParser InputSource input = entityResolver.resolveEntity(publicId, systemId); if (input != null) - return input.getByteStream(); + { + InputStream in = input.getByteStream(); + if (in == null) + { + String newSystemId = input.getSystemId(); + if (newSystemId != null && !newSystemId.equals(systemId)) + in = XMLParser.resolve(newSystemId); + } + return in; + } } catch (SAXException e) { diff --git a/gnu/xml/stream/UnicodeReader.java b/gnu/xml/stream/UnicodeReader.java index c38516c30..8a1f1b83e 100644 --- a/gnu/xml/stream/UnicodeReader.java +++ b/gnu/xml/stream/UnicodeReader.java @@ -45,7 +45,7 @@ import java.io.Reader; * * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a> */ -class UnicodeReader +public class UnicodeReader { final Reader in; diff --git a/gnu/xml/stream/XIncludeFilter.java b/gnu/xml/stream/XIncludeFilter.java index e151ac69d..6d12bb3b2 100644 --- a/gnu/xml/stream/XIncludeFilter.java +++ b/gnu/xml/stream/XIncludeFilter.java @@ -42,6 +42,7 @@ import java.io.InputStreamReader; import java.io.IOException; import java.io.Reader; import java.net.HttpURLConnection; +import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import java.util.HashSet; @@ -121,7 +122,17 @@ class XIncludeFilter boolean expandERefs) { super(reader); - this.systemId = XMLParser.absolutize(null, systemId); + try + { + this.systemId = XMLParser.absolutize(null, systemId); + } + catch (MalformedURLException e) + { + RuntimeException e2 = new RuntimeException("unsupported URL: " + + systemId); + e2.initCause(e); + throw e2; + } this.namespaceAware = namespaceAware; this.validating = validating; this.expandERefs = expandERefs; diff --git a/gnu/xml/stream/XMLParser.java b/gnu/xml/stream/XMLParser.java index 3545ff32d..727dcc437 100644 --- a/gnu/xml/stream/XMLParser.java +++ b/gnu/xml/stream/XMLParser.java @@ -1484,7 +1484,6 @@ public class XMLParser { if (!externalEntities) return; - InputStream in = null; String url = absolutize(input.systemId, ids.systemId); // Check for recursion for (Iterator i = inputStack.iterator(); i.hasNext(); ) @@ -1497,7 +1496,8 @@ public class XMLParser } if (name == null || "".equals(name)) report = false; - if (in == null && url != null && resolver != null) + InputStream in = null; + if (resolver != null) { if (resolver instanceof XMLResolver2) in = ((XMLResolver2) resolver).resolve(ids.publicId, url); @@ -1535,12 +1535,13 @@ public class XMLParser * @param base the current base URL * @param href the (absolute or relative) URL to resolve */ - static String absolutize(String base, String href) + public static String absolutize(String base, String href) + throws MalformedURLException { if (href == null) return null; int ci = href.indexOf(':'); - if (ci > 1 && isLowercaseAscii(href.substring(0, ci))) + if (ci > 1 && isURLScheme(href.substring(0, ci))) { // href is absolute already return href; @@ -1565,40 +1566,23 @@ public class XMLParser if (!base.endsWith("/")) base += "/"; } - if (href.startsWith("/")) - { - if (base.startsWith("file:")) - return "file://" + href; - int i = base.indexOf("://"); - if (i != -1) - { - i = base.indexOf('/', i + 3); - if (i != -1) - base = base.substring(0, i); - } - } - else - { - while (href.startsWith("..")) - { - int i = base.lastIndexOf('/', base.length() - 2); - if (i != -1) - base = base.substring(0, i + 1); - href = href.substring(2); - if (href.startsWith("/")) - href = href.substring(1); - } - } - return base + href; + return new URL(new URL(base), href).toString(); } - private static boolean isLowercaseAscii(String text) + /** + * Indicates whether the specified characters match the scheme portion of + * a URL. + * @see RFC 1738 section 2.1 + */ + private static boolean isURLScheme(String text) { int len = text.length(); for (int i = 0; i < len; i++) { char c = text.charAt(i); - if (c < 97 || c > 122) + if (c == '+' || c == '.' || c == '-') + continue; + if (c < 65 || (c > 90 && c < 97) || c > 122) return false; } return true; @@ -1607,7 +1591,7 @@ public class XMLParser /** * Returns an input stream for the given URL. */ - private InputStream resolve(String url) + static InputStream resolve(String url) throws IOException { try @@ -1618,6 +1602,12 @@ public class XMLParser { return null; } + catch (IOException e) + { + IOException e2 = new IOException("error resolving " + url); + e2.initCause(e); + throw e2; + } } /** diff --git a/gnu/xml/transform/ApplyTemplatesNode.java b/gnu/xml/transform/ApplyTemplatesNode.java index 86d9b7d0e..38b605a07 100644 --- a/gnu/xml/transform/ApplyTemplatesNode.java +++ b/gnu/xml/transform/ApplyTemplatesNode.java @@ -76,7 +76,7 @@ final class ApplyTemplatesNode TemplateNode clone(Stylesheet stylesheet) { - int len = sortKeys.size(); + int len = sortKeys != null ? sortKeys.size() : 0; List sortKeys2 = new ArrayList(len); for (int i = 0; i < len; i++) sortKeys2.add(((Key) sortKeys.get(i)).clone(stylesheet)); |