diff options
| author | mark <mark@138bc75d-0d04-0410-961f-82ee72b054a4> | 2006-03-10 21:46:48 +0000 |
|---|---|---|
| committer | mark <mark@138bc75d-0d04-0410-961f-82ee72b054a4> | 2006-03-10 21:46:48 +0000 |
| commit | ce57ab760f69de6db452def7ffbf5b114a2d8694 (patch) | |
| tree | ea38c56431c5d4528fb54254c3f8e50f517bede3 /libjava/classpath/gnu/javax/crypto/sasl/srp | |
| parent | 50996fe55769882de3f410896032c887f0ff0d04 (diff) | |
| download | ppe42-gcc-ce57ab760f69de6db452def7ffbf5b114a2d8694.tar.gz ppe42-gcc-ce57ab760f69de6db452def7ffbf5b114a2d8694.zip | |
Imported GNU Classpath 0.90
* scripts/makemake.tcl: Set gnu/java/awt/peer/swing to ignore.
* gnu/classpath/jdwp/VMFrame.java (SIZE): New constant.
* java/lang/VMCompiler.java: Use gnu.java.security.hash.MD5.
* java/lang/Math.java: New override file.
* java/lang/Character.java: Merged from Classpath.
(start, end): Now 'int's.
(canonicalName): New field.
(CANONICAL_NAME, NO_SPACES_NAME, CONSTANT_NAME): New constants.
(UnicodeBlock): Added argument.
(of): New overload.
(forName): New method.
Updated unicode blocks.
(sets): Updated.
* sources.am: Regenerated.
* Makefile.in: Likewise.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@111942 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libjava/classpath/gnu/javax/crypto/sasl/srp')
13 files changed, 5030 insertions, 0 deletions
diff --git a/libjava/classpath/gnu/javax/crypto/sasl/srp/CALG.java b/libjava/classpath/gnu/javax/crypto/sasl/srp/CALG.java new file mode 100644 index 00000000000..6215783d6a9 --- /dev/null +++ b/libjava/classpath/gnu/javax/crypto/sasl/srp/CALG.java @@ -0,0 +1,292 @@ +/* CALG.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a 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 of the License, 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; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, 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.javax.crypto.sasl.srp; + +import gnu.java.security.Registry; +import gnu.javax.crypto.assembly.Assembly; +import gnu.javax.crypto.assembly.Cascade; +import gnu.javax.crypto.assembly.Direction; +import gnu.javax.crypto.assembly.Stage; +import gnu.javax.crypto.assembly.Transformer; +import gnu.javax.crypto.assembly.TransformerException; +import gnu.javax.crypto.cipher.CipherFactory; +import gnu.javax.crypto.cipher.IBlockCipher; +import gnu.javax.crypto.mode.IMode; +import gnu.javax.crypto.mode.ModeFactory; +import gnu.javax.crypto.pad.IPad; +import gnu.javax.crypto.pad.PadFactory; +import gnu.javax.crypto.sasl.ConfidentialityException; + +import java.util.HashMap; + +import javax.security.sasl.SaslException; + +/** + * <p>A Factory class that returns CALG (Confidentiality Algorithm) instances + * that operate as described in the draft-burdis-cat-sasl-srp-08.</p> + * + * <p>The designated CALG block cipher should be used in OFB (Output Feedback + * Block) mode in the ISO variant, as described in <i>The Handbook of Applied + * Cryptography</i>, algorithm 7.20.</p> + * + * <p>Let <code>k</code> be the block size of the chosen symmetric key block + * cipher algorithm; e.g. for AES this is <code>128</code> bits or <code>16</code> + * octets. The OFB mode used shall be of length/size <code>k</code>.</p> + * + * <p>It is recommended that block ciphers operating in OFB mode be used with an + * Initial Vector (the mode's IV). In such a mode of operation - OFB with key + * re-use - the IV need not be secret. For the mechanism in question the IVs + * shall be a random octet sequence of <code>k</code> bytes.</p> + * + * The input data to the confidentiality protection algorithm shall be + * a multiple of the symmetric cipher block size <code>k</code>. When the input + * length is not a multiple of <code>k</code> octets, the data shall be padded + * according to the following scheme:</p> + * + * <p>Assuming the length of the input is <code>l</code> octets, + * <code>(k - (l mod k))</code> octets, all having the value + * <code>(k - (l mod k))</code>, shall be appended to the original data. In + * other words, the input is padded at the trailing end with one of the + * following sequences:</p> + * + * <pre> + * + * 01 -- if l mod k = k-1 + * 02 02 -- if l mod k = k-2 + * ... + * ... + * ... + * k k ... k k -- if l mod k = 0 + *</pre> + * + * <p>The padding can be removed unambiguously since all input is padded and no + * padding sequence is a suffix of another. This padding method is well-defined + * if and only if <code>k < 256</code> octets, which is the case with + * symmetric key block ciphers today, and in the forseeable future.</p> + */ +public final class CALG +{ + + // Constants and variables + // -------------------------------------------------------------------------- + + private Assembly assembly; + + private Object modeNdx; // initialisation key of the cascade's attributes + + private int blockSize; // the underlying cipher's blocksize == IV length + + private int keySize; // the underlying cipher's key size (in bytes). + + // Constructor(s) + // -------------------------------------------------------------------------- + + /** Private constructor to enforce instantiation through Factory method. */ + private CALG(final int blockSize, final int keySize, final Object modeNdx, + final Assembly assembly) + { + super(); + + this.blockSize = blockSize; + this.keySize = keySize; + this.modeNdx = modeNdx; + this.assembly = assembly; + } + + // Class methods + // ------------------------------------------------------------------------- + + /** + * <p>Returns an instance of a SASL-SRP CALG implementation.</p> + * + * @param algorithm the name of the symmetric cipher algorithm. + * @return an instance of this object. + */ + static synchronized CALG getInstance(final String algorithm) + { + final IBlockCipher cipher = CipherFactory.getInstance(algorithm); + final int blockSize = cipher.defaultBlockSize(); + final int keySize = cipher.defaultKeySize(); + final Cascade ofbCipher = new Cascade(); + final Object modeNdx = ofbCipher.append(Stage.getInstance( + ModeFactory.getInstance( + Registry.OFB_MODE, + cipher, + blockSize), + Direction.FORWARD)); + final IPad pkcs7 = PadFactory.getInstance(Registry.PKCS7_PAD); + // the passed IV may be longer that what we need. ensure correct length + // byte[] realIV = null; + // if (iv.length == blockSize) { + // realIV = iv; + // } else { + // realIV = new byte[blockSize]; + // if (iv.length > blockSize) { + // System.arraycopy(iv, 0, realIV, 0, blockSize); + // } else { // shouldnt happen + // System.arraycopy(iv, 0, realIV, 0, iv.length); + // } + // } + + // HashMap modeAttributes = new HashMap(); + // modeAttributes.put(IBlockCipher.KEY_MATERIAL, K.clone()); + // modeAttributes.put(IMode.IV, realIV); + + final Assembly asm = new Assembly(); + asm.addPreTransformer(Transformer.getCascadeTransformer(ofbCipher)); + asm.addPreTransformer(Transformer.getPaddingTransformer(pkcs7)); + + // HashMap attributes = new HashMap(); + // attributes.put(Assembly.DIRECTION, dir); + // attributes.put(modeNdx, modeAttributes); + // try { + // asm.init(attributes); + // } catch (TransformerException x) { + // throw new SaslException("getInstance()", x); + // } + + return new CALG(blockSize, keySize, modeNdx, asm); + } + + // Instance methods + // ------------------------------------------------------------------------- + + /** + * <p>Initialises a SASL-SRP CALG implementation.</p> + * + * @param kdf the key derivation function. + * @param iv the initial vector value to use. + * @param dir whether this CALG is used for encryption or decryption. + */ + // public void init(byte[] K, byte[] iv, Direction dir) throws SaslException { + public void init(final KDF kdf, final byte[] iv, final Direction dir) + throws SaslException + { + // IBlockCipher cipher = CipherFactory.getInstance(algorithm); + // int blockSize = cipher.defaultBlockSize(); + // Cascade ofbCipher = new Cascade(); + // Object modeNdx = ofbCipher.append( + // Stage.getInstace( + // ModeFactory.getInstance(Registry.OFB_MODE, cipher, blockSize), + // Direction.FORWARD)); + // IPad pkcs7 = PadFactory.getInstance(Registry.PKCS7_PAD); + // the passed IV may be longer that what we need. ensure correct length + final byte[] realIV; + if (iv.length == blockSize) + { + realIV = iv; + } + else + { + realIV = new byte[blockSize]; + if (iv.length > blockSize) + { + System.arraycopy(iv, 0, realIV, 0, blockSize); + } + else + { // shouldnt happen + System.arraycopy(iv, 0, realIV, 0, iv.length); + } + } + + final HashMap modeAttributes = new HashMap(); + // modeAttributes.put(IBlockCipher.KEY_MATERIAL, K.clone()); + final byte[] sk = kdf.derive(keySize); + modeAttributes.put(IBlockCipher.KEY_MATERIAL, sk); + //System.out.println("**** Initialised CALG with: "+gnu.crypto.util.Util.dumpString(sk)); + modeAttributes.put(IMode.IV, realIV); + + // Assembly asm = new Assembly(); + // asm.addPreTransformer(Transformer.getCascadeTransformer(ofbCipher)); + // asm.addPreTransformer(Transformer.getPaddingTransformer(pkcs7)); + + final HashMap attributes = new HashMap(); + attributes.put(Assembly.DIRECTION, dir); + attributes.put(modeNdx, modeAttributes); + try + { + // asm.init(attributes); + assembly.init(attributes); + } + catch (TransformerException x) + { + throw new SaslException("getInstance()", x); + } + + // return new CALG(asm); + } + + /** + * <p>Encrypts or decrypts, depending on the mode already set, a designated + * array of bytes and returns the result.</p> + * + * @param data the data to encrypt/decrypt. + * @return the decrypted/encrypted result. + * @throws ConfidentialityException if an exception occurs duirng the process. + */ + public byte[] doFinal(final byte[] data) throws ConfidentialityException + { + return doFinal(data, 0, data.length); + } + + /** + * <p>Encrypts or decrypts, depending on the mode already set, a designated + * array of bytes and returns the result.</p> + * + * @param data the data to encrypt/decrypt. + * @param offset where to start in <code>data</code>. + * @param length how many bytes to consider in <code>data</code>. + * @return the decrypted/encrypted result. + * @throws ConfidentialityException if an exception occurs duirng the process. + */ + public byte[] doFinal(final byte[] data, final int offset, final int length) + throws ConfidentialityException + { + final byte[] result; + try + { + result = assembly.lastUpdate(data, offset, length); + } + catch (TransformerException x) + { + throw new ConfidentialityException("doFinal()", x); + } + return result; + } +}
\ No newline at end of file diff --git a/libjava/classpath/gnu/javax/crypto/sasl/srp/ClientStore.java b/libjava/classpath/gnu/javax/crypto/sasl/srp/ClientStore.java new file mode 100644 index 00000000000..ce16f4aa75d --- /dev/null +++ b/libjava/classpath/gnu/javax/crypto/sasl/srp/ClientStore.java @@ -0,0 +1,173 @@ +/* ClientStore.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a 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 of the License, 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; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, 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.javax.crypto.sasl.srp; + +import java.util.HashMap; + +/** + * <p>The client-side implementation of the SRP security context store.</p> + */ +public class ClientStore +{ + + // Constants and variables + // ------------------------------------------------------------------------- + + /** The underlying singleton. */ + private static ClientStore singleton = null; + + /** The map of uid --> SASL Security Context record. */ + private static final HashMap uid2ssc = new HashMap(); + + /** The map of sid --> Session timing record. */ + private static final HashMap uid2ttl = new HashMap(); + + /** A synchronisation lock. */ + private static final Object lock = new Object(); + + // Constructor(s) + // ------------------------------------------------------------------------- + + /** Private constructor to enforce Singleton pattern. */ + private ClientStore() + { + super(); + + // TODO: add a cleaning timer thread + } + + // Class methods + // ------------------------------------------------------------------------- + + /** + * <p>Returns the classloader Singleton.</p> + * + * @return the classloader Singleton instance. + */ + static synchronized final ClientStore instance() + { + if (singleton == null) + { + singleton = new ClientStore(); + } + return singleton; + } + + // Instance methods + // ------------------------------------------------------------------------- + + /** + * <p>Returns a boolean flag indicating if the designated client's session is + * still alive or not.</p> + * + * @param uid the identifier of the client whose session to check. + * @return <code>true</code> if the designated client's session is still + * alive. <code>false</code> otherwise. + */ + boolean isAlive(final String uid) + { + final boolean result; + synchronized (lock) + { + final Object obj = uid2ssc.get(uid); + result = (obj != null); + if (result) + { // is it still alive? + final StoreEntry sto = (StoreEntry) uid2ttl.get(uid); + if (!sto.isAlive()) + { // invalidate it + uid2ssc.remove(uid); + uid2ttl.remove(uid); + } + } + } + return result; + } + + /** + * <p>Records a mapping between a client's unique identifier and its security + * context.</p> + * + * @param uid the unique identifier of the SRP client for which the session + * is to be cached. + * @param ttl the session's Time-To-Live indicator (in seconds). + * @param ctx the client's security context. + */ + void cacheSession(final String uid, final int ttl, final SecurityContext ctx) + { + synchronized (lock) + { + uid2ssc.put(uid, ctx); + uid2ttl.put(uid, new StoreEntry(ttl)); + } + } + + /** + * <p>Removes the mapping between the designated SRP client unique identifier + * and the its session security context (and other timing information).</p> + * + * @param uid the identifier of the client whose session is to invalidate. + */ + void invalidateSession(final String uid) + { + synchronized (lock) + { + uid2ssc.remove(uid); + uid2ttl.remove(uid); + } + } + + /** + * <p>Returns an SRP client's security context record mapped by that client's + * unique identifier.</p> + * + * @param uid the identifier of the client whose session is to restore. + * @return the SRP client's security context. + */ + SecurityContext restoreSession(final String uid) + { + final SecurityContext result; + synchronized (lock) + { + result = (SecurityContext) uid2ssc.remove(uid); + uid2ttl.remove(uid); + } + return result; + } +}
\ No newline at end of file diff --git a/libjava/classpath/gnu/javax/crypto/sasl/srp/IALG.java b/libjava/classpath/gnu/javax/crypto/sasl/srp/IALG.java new file mode 100644 index 00000000000..cfaf22b1e02 --- /dev/null +++ b/libjava/classpath/gnu/javax/crypto/sasl/srp/IALG.java @@ -0,0 +1,159 @@ +/* IALG.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a 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 of the License, 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; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, 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.javax.crypto.sasl.srp; + +import gnu.javax.crypto.mac.IMac; +import gnu.javax.crypto.mac.MacFactory; + +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.HashMap; + +import javax.security.sasl.SaslException; + +/** + * <p>A Factory class that returns IALG (Integrity Algorithm) instances that + * operate as described in the draft-burdis-cat-sasl-srp-04 and later.</p> + * + * @version $Revision: 1.1 $ + */ +public final class IALG implements Cloneable +{ + + // Constants and variables + // -------------------------------------------------------------------------- + + private IMac hmac; + + // Constructor(s) + // -------------------------------------------------------------------------- + + /** Private constructor to enforce instantiation through Factory method. */ + private IALG(final IMac hmac) + { + super(); + + this.hmac = hmac; + } + + // Class methods + // ------------------------------------------------------------------------- + + /** + * <p>Returns an instance of a SASL-SRP IALG implementation.</p> + * + * @param algorithm the name of the HMAC algorithm. + * @return an instance of this object. + */ + static synchronized IALG getInstance(final String algorithm) + throws SaslException + { + final IMac hmac; + hmac = MacFactory.getInstance(algorithm); + if (hmac == null) + { + throw new SaslException("getInstance()", + new NoSuchAlgorithmException(algorithm)); + } + // try { + // byte[] sk = (byte[]) K.clone(); + // HashMap map = new HashMap(); + // map.put(IMac.MAC_KEY_MATERIAL, sk); + // hmac.init(map); + // } catch (InvalidKeyException x) { + // throw new SaslException("getInstance()", x); + // } + return new IALG(hmac); + } + + // Instance methods + // ------------------------------------------------------------------------- + + // Cloneable interface implementation -------------------------------------- + + public Object clone() throws CloneNotSupportedException + { + return new IALG((IMac) hmac.clone()); + } + + // other methdds ----------------------------------------------------------- + + // public void init(final byte[] K) throws SaslException { + public void init(final KDF kdf) throws SaslException + { + try + { + // final byte[] sk = (byte[]) K.clone(); + final byte[] sk = kdf.derive(hmac.macSize()); + final HashMap map = new HashMap(); + map.put(IMac.MAC_KEY_MATERIAL, sk); + hmac.init(map); + //System.out.println("**** Initialised IALG with: "+gnu.crypto.util.Util.dumpString(sk)); + } + catch (InvalidKeyException x) + { + throw new SaslException("getInstance()", x); + } + } + + public void update(final byte[] data) + { + hmac.update(data, 0, data.length); + } + + public void update(final byte[] data, final int offset, final int length) + { + hmac.update(data, offset, length); + } + + public byte[] doFinal() + { + return hmac.digest(); + } + + /** + * <p>Returns the length (in bytes) of this SASL SRP Integrity Algorithm.</p> + * + * @return the length, in bytes, of this integrity protection algorithm. + */ + public int length() + { + return hmac.macSize(); + } +}
\ No newline at end of file diff --git a/libjava/classpath/gnu/javax/crypto/sasl/srp/KDF.java b/libjava/classpath/gnu/javax/crypto/sasl/srp/KDF.java new file mode 100644 index 00000000000..0d5eeacd182 --- /dev/null +++ b/libjava/classpath/gnu/javax/crypto/sasl/srp/KDF.java @@ -0,0 +1,169 @@ +/* KDF.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a 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 of the License, 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; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, 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.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; + +/** + * <p>The SASL-SRP KDF implementation, which is also used, depending on how it + * was instantiated, as a secure Pseudo Random Number Generator.</p> + */ +public class KDF +{ + + // Constants and variables + // ------------------------------------------------------------------------- + + private static final int AES_BLOCK_SIZE = 16; // default block size for the AES + + 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. */ + private UMacGenerator umac = null; + + // Constructor(s) + // ------------------------------------------------------------------------- + + /** + * <p>Constructs an instance of the <code>KDF</code> initialised with the + * designated shared secret bytes.</p> + * + * @param keyMaterial the SASL SRP shared secret (K) bytes. + */ + private KDF(final byte[] keyMaterial, final int ndx) + { + super(); + + // if (ndx != 0) { + // this.keyMaterial = (byte[]) keyMaterial.clone(); + // } + final HashMap map = new HashMap(); + map.put(UMacGenerator.CIPHER, Registry.AES_CIPHER); + map.put(UMacGenerator.INDEX, new Integer(ndx)); + map.put(IBlockCipher.CIPHER_BLOCK_SIZE, new Integer(AES_BLOCK_SIZE)); + final byte[] key = new byte[AES_KEY_SIZE]; + System.arraycopy(keyMaterial, 0, key, 0, AES_KEY_SIZE); + map.put(IBlockCipher.KEY_MATERIAL, key); + + umac = new UMacGenerator(); + umac.init(map); + //System.out.println("**** Initialised KDF with: "+gnu.crypto.util.Util.dumpString(key)); + } + + // Class methods + // ------------------------------------------------------------------------- + + /** + * <p>A Factory mehod that returns an instance of a <code>KDF</code> based on + * supplied seed data.</p> + * + * @param K the SASL SRP shared secret for a <code>KDF</code> to be used for + * <i>CALG</i> and <i>IALG</i> setup. <code>null</code> otherwise. + * @return an instance of a <code>KDF</code>. + */ + static final KDF getInstance(final byte[] K) + { + int ndx = -1; + final byte[] keyMaterial; + if (K != null) + { + keyMaterial = K; + ndx = 0; + } + else + { + keyMaterial = new byte[AES_BLOCK_SIZE]; + 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 + // ------------------------------------------------------------------------- + + /** + * <p>Returns a designated number of bytes suitable for use in the SASL SRP + * mechanism.</p> + * + * @param length the number of bytes needed. + * @return a byte array containing the generated/selected bytes. + */ + public synchronized byte[] derive(final int length) + { + final byte[] result = new byte[length]; + // if (keyMaterial == null || length > keyMaterial.length) { + try + { + umac.nextBytes(result, 0, length); + } + catch (IllegalStateException x) + { // should not happen + x.printStackTrace(System.err); + } + catch (LimitReachedException x) + { // idem + x.printStackTrace(System.err); + } + // } else { + // System.arraycopy(keyMaterial, 0, result, 0, length); + // } + + return result; + } +}
\ No newline at end of file diff --git a/libjava/classpath/gnu/javax/crypto/sasl/srp/PasswordFile.java b/libjava/classpath/gnu/javax/crypto/sasl/srp/PasswordFile.java new file mode 100644 index 00000000000..1628a4167ab --- /dev/null +++ b/libjava/classpath/gnu/javax/crypto/sasl/srp/PasswordFile.java @@ -0,0 +1,699 @@ +/* PasswordFile.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a 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 of the License, 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; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, 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.javax.crypto.sasl.srp; + +import gnu.java.security.Registry; +import gnu.java.security.hash.IMessageDigest; +import gnu.java.security.util.Util; +import gnu.javax.crypto.key.srp6.SRPAlgorithm; +import gnu.javax.crypto.sasl.NoSuchUserException; +import gnu.javax.crypto.sasl.UserAlreadyExistsException; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; +import java.math.BigInteger; +import java.util.HashMap; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.StringTokenizer; + +/** + * <p>The implementation of SRP password files.</p> + * + * <p>For SRP, there are three (3) files: + * <ol> + * <li>The password configuration file: tpasswd.conf. It contains the pairs + * <N,g> indexed by a number for each pair used for a user. By default, + * this file's pathname is constructed from the base password file pathname + * by prepending it with the ".conf" suffix.</li> + * + * <li>The base password file: tpasswd. It contains the related password + * entries for all the users with values computed using SRP's default + * message digest algorithm: SHA-1 (with 160-bit output block size).</li> + * + * <li>The extended password file: tpasswd2. Its name, by default, is + * constructed by adding the suffix "2" to the fully qualified pathname of + * the base password file. It contains, in addition to the same fields as + * the base password file, albeit with a different verifier value, an extra + * field identifying the message digest algorithm used to compute this + * (verifier) value.</li> + * </ol></p> + * + * <p>This implementation assumes the following message digest algorithm codes: + * <ul> + * <li>0: the default hash algorithm, which is SHA-1 (or its alias SHA-160).</li> + * <li>1: MD5.</li> + * <li>2: RIPEMD-128.</li> + * <li>3: RIPEMD-160.</li> + * <li>4: SHA-256.</li> + * <li>5: SHA-384.</li> + * <li>6: SHA-512.</li> + * </ul></p> + * + * <p><b>IMPORTANT:</b> This method computes the verifiers as described in + * RFC-2945, which differs from the description given on the web page for + * SRP-6.</p> + * + * <p>Reference:</p> + * <ol> + * <li><a href="http://srp.stanford.edu/design.html">SRP Protocol Design</a><br> + * Thomas J. Wu.</li> + * </ol> + */ +public class PasswordFile +{ + + // Constants and variables + // ------------------------------------------------------------------------- + + // names of property keys used in this class + private static final String USER_FIELD = "user"; + + private static final String VERIFIERS_FIELD = "verifier"; + + private static final String SALT_FIELD = "salt"; + + private static final String CONFIG_FIELD = "config"; + + private static String DEFAULT_FILE; + static + { + DEFAULT_FILE = System.getProperty(SRPRegistry.PASSWORD_FILE, + SRPRegistry.DEFAULT_PASSWORD_FILE); + } + + /** The SRP algorithm instances used by this object. */ + private static final HashMap srps; + static + { + final HashMap map = new HashMap(SRPRegistry.SRP_ALGORITHMS.length); + // The first entry MUST exist. The others are optional. + map.put("0", SRP.instance(SRPRegistry.SRP_ALGORITHMS[0])); + for (int i = 1; i < SRPRegistry.SRP_ALGORITHMS.length; i++) + { + try + { + map.put(String.valueOf(i), + SRP.instance(SRPRegistry.SRP_ALGORITHMS[i])); + } + catch (Exception x) + { + System.err.println("Ignored: " + x); + x.printStackTrace(System.err); + } + } + srps = map; + } + + private String confName, pwName, pw2Name; + + private File configFile, passwdFile, passwd2File; + + private long lastmodPasswdFile, lastmodPasswd2File; + + private HashMap entries = new HashMap(); + + private HashMap configurations = new HashMap(); + + // default N values to use when creating a new password.conf file + private static final BigInteger[] Nsrp = new BigInteger[] { + SRPAlgorithm.N_2048, + SRPAlgorithm.N_1536, + SRPAlgorithm.N_1280, + SRPAlgorithm.N_1024, + SRPAlgorithm.N_768, + SRPAlgorithm.N_640, + SRPAlgorithm.N_512 }; + + // Constructor(s) + // ------------------------------------------------------------------------- + + public PasswordFile() throws IOException + { + this(DEFAULT_FILE); + } + + public PasswordFile(final File pwFile) throws IOException + { + this(pwFile.getAbsolutePath()); + } + + public PasswordFile(final String pwName) throws IOException + { + this(pwName, pwName + "2", pwName + ".conf"); + } + + public PasswordFile(final String pwName, final String confName) + throws IOException + { + this(pwName, pwName + "2", confName); + } + + public PasswordFile(final String pwName, final String pw2Name, + final String confName) throws IOException + { + super(); + + this.pwName = pwName; + this.pw2Name = pw2Name; + this.confName = confName; + + readOrCreateConf(); + update(); + } + + // Class methods + // ------------------------------------------------------------------------- + + /** + * <p>Returns a string representing the decimal value of an integer + * identifying the message digest algorithm to use for the SRP computations. + * </p> + * + * @param mdName the canonical name of a message digest algorithm. + * @return a string representing the decimal value of an ID for that + * algorithm. + */ + private static final String nameToID(final String mdName) + { + if (Registry.SHA_HASH.equalsIgnoreCase(mdName) + || Registry.SHA1_HASH.equalsIgnoreCase(mdName) + || Registry.SHA160_HASH.equalsIgnoreCase(mdName)) + { + return "0"; + } + else if (Registry.MD5_HASH.equalsIgnoreCase(mdName)) + { + return "1"; + } + else if (Registry.RIPEMD128_HASH.equalsIgnoreCase(mdName)) + { + return "2"; + } + else if (Registry.RIPEMD160_HASH.equalsIgnoreCase(mdName)) + { + return "3"; + } + else if (Registry.SHA256_HASH.equalsIgnoreCase(mdName)) + { + return "4"; + } + else if (Registry.SHA384_HASH.equalsIgnoreCase(mdName)) + { + return "5"; + } + else if (Registry.SHA512_HASH.equalsIgnoreCase(mdName)) + { + return "6"; + } + return "0"; + } + + // SRP password configuration file methods --------------------------------- + + /** + * <p>Checks if the current configuration file contains the <N, g> pair + * for the designated <code>index</code>.</p> + * + * @param index a string representing 1-digit identification of an <N, g> + * pair used. + * @return <code>true</code> if the designated <code>index</code> is that of + * a known <N, g> pair, and <code>false</code> otherwise. + * @throws IOException if an exception occurs during the process. + * @see SRPRegistry#N_2048_BITS + * @see SRPRegistry#N_1536_BITS + * @see SRPRegistry#N_1280_BITS + * @see SRPRegistry#N_1024_BITS + * @see SRPRegistry#N_768_BITS + * @see SRPRegistry#N_640_BITS + * @see SRPRegistry#N_512_BITS + */ + public synchronized boolean containsConfig(final String index) + throws IOException + { + checkCurrent(); + return configurations.containsKey(index); + } + + /** + * <p>Returns a pair of strings representing the pair of <code>N</code> and + * <code>g</code> MPIs for the designated <code>index</code>.</p> + * + * @param index a string representing 1-digit identification of an <N, g> + * pair to look up. + * @return a pair of strings, arranged in an array, where the first (at index + * position #0) is the repesentation of the MPI <code>N</code>, and the + * second (at index position #1) is the representation of the MPI + * <code>g</code>. If the <code>index</code> refers to an unknown pair, then + * an empty string array is returned. + * @throws IOException if an exception occurs during the process. + */ + public synchronized String[] lookupConfig(final String index) + throws IOException + { + checkCurrent(); + String[] result = null; + if (configurations.containsKey(index)) + { + result = (String[]) configurations.get(index); + } + return result; + } + + // SRP base and extended password configuration files methods -------------- + + public synchronized boolean contains(final String user) throws IOException + { + checkCurrent(); + return entries.containsKey(user); + } + + public synchronized void add(final String user, final String passwd, + final byte[] salt, final String index) + throws IOException + { + checkCurrent(); + if (entries.containsKey(user)) + { + throw new UserAlreadyExistsException(user); + } + final HashMap fields = new HashMap(4); + fields.put(USER_FIELD, user); // 0 + fields.put(VERIFIERS_FIELD, newVerifiers(user, salt, passwd, index)); // 1 + fields.put(SALT_FIELD, Util.toBase64(salt)); // 2 + fields.put(CONFIG_FIELD, index); // 3 + entries.put(user, fields); + savePasswd(); + } + + public synchronized void changePasswd(final String user, final String passwd) + throws IOException + { + checkCurrent(); + if (!entries.containsKey(user)) + { + throw new NoSuchUserException(user); + } + final HashMap fields = (HashMap) entries.get(user); + final byte[] salt; + try + { + salt = Util.fromBase64((String) fields.get(SALT_FIELD)); + } + catch (NumberFormatException x) + { + throw new IOException("Password file corrupt"); + } + final String index = (String) fields.get(CONFIG_FIELD); + fields.put(VERIFIERS_FIELD, newVerifiers(user, salt, passwd, index)); + entries.put(user, fields); + savePasswd(); + } + + public synchronized void savePasswd() throws IOException + { + final FileOutputStream f1 = new FileOutputStream(passwdFile); + final FileOutputStream f2 = new FileOutputStream(passwd2File); + PrintWriter pw1 = null; + PrintWriter pw2 = null; + try + { + pw1 = new PrintWriter(f1, true); + pw2 = new PrintWriter(f2, true); + this.writePasswd(pw1, pw2); + } + finally + { + if (pw1 != null) + { + try + { + pw1.flush(); + } + finally + { + pw1.close(); + } + } + if (pw2 != null) + { + try + { + pw2.flush(); + } + finally + { + pw2.close(); + } + } + try + { + f1.close(); + } + catch (IOException ignored) + { + } + try + { + f2.close(); + } + catch (IOException ignored) + { + } + } + lastmodPasswdFile = passwdFile.lastModified(); + lastmodPasswd2File = passwd2File.lastModified(); + } + + /** + * <p>Returns the triplet: verifier, salt and configuration file index, of a + * designated user, and a designated message digest algorithm name, as an + * array of strings.</p> + * + * @param user the username. + * @param mdName the canonical name of the SRP's message digest algorithm. + * @return a string array containing, in this order, the BASE-64 encodings of + * the verifier, the salt and the index in the password configuration file of + * the MPIs N and g of the designated user. + */ + public synchronized String[] lookup(final String user, final String mdName) + throws IOException + { + checkCurrent(); + if (!entries.containsKey(user)) + { + throw new NoSuchUserException(user); + } + final HashMap fields = (HashMap) entries.get(user); + final HashMap verifiers = (HashMap) fields.get(VERIFIERS_FIELD); + final String salt = (String) fields.get(SALT_FIELD); + final String index = (String) fields.get(CONFIG_FIELD); + final String verifier = (String) verifiers.get(nameToID(mdName)); + return new String[] { verifier, salt, index }; + } + + // Other instance methods -------------------------------------------------- + + private synchronized void readOrCreateConf() throws IOException + { + configurations.clear(); + final FileInputStream fis; + configFile = new File(confName); + try + { + fis = new FileInputStream(configFile); + readConf(fis); + } + catch (FileNotFoundException x) + { // create a default one + final String g = Util.toBase64(Util.trim(new BigInteger("2"))); + String index, N; + for (int i = 0; i < Nsrp.length; i++) + { + index = String.valueOf(i + 1); + N = Util.toBase64(Util.trim(Nsrp[i])); + configurations.put(index, new String[] { N, g }); + } + FileOutputStream f0 = null; + PrintWriter pw0 = null; + try + { + f0 = new FileOutputStream(configFile); + pw0 = new PrintWriter(f0, true); + this.writeConf(pw0); + } + finally + { + if (pw0 != null) + { + pw0.close(); + } + else if (f0 != null) + { + f0.close(); + } + } + } + } + + private void readConf(final InputStream in) throws IOException + { + final BufferedReader din = new BufferedReader(new InputStreamReader(in)); + String line, index, N, g; + StringTokenizer st; + while ((line = din.readLine()) != null) + { + st = new StringTokenizer(line, ":"); + try + { + index = st.nextToken(); + N = st.nextToken(); + g = st.nextToken(); + } + catch (NoSuchElementException x) + { + throw new IOException("SRP password configuration file corrupt"); + } + configurations.put(index, new String[] { N, g }); + } + } + + private void writeConf(final PrintWriter pw) + { + String ndx; + String[] mpi; + StringBuffer sb; + for (Iterator it = configurations.keySet().iterator(); it.hasNext();) + { + ndx = (String) it.next(); + mpi = (String[]) configurations.get(ndx); + sb = new StringBuffer(ndx).append(":").append(mpi[0]).append(":").append( + mpi[1]); + pw.println(sb.toString()); + } + } + + /** + * <p>Compute the new verifiers for the designated username and password.</p> + * + * <p><b>IMPORTANT:</b> This method computes the verifiers as described in + * RFC-2945, which differs from the description given on the web page for + * SRP-6.</p> + * + * @param user the user's name. + * @param s the user's salt. + * @param password the user's password + * @param index the index of the <N, g> pair to use for this user. + * @return a {@link java.util.Map} of user verifiers. + * @throws UnsupportedEncodingException if the US-ASCII decoder is not + * available on this platform. + */ + private HashMap newVerifiers(final String user, final byte[] s, + final String password, final String index) + throws UnsupportedEncodingException + { + // to ensure inter-operability with non-java tools + final String[] mpi = (String[]) configurations.get(index); + final BigInteger N = new BigInteger(1, Util.fromBase64(mpi[0])); + final BigInteger g = new BigInteger(1, Util.fromBase64(mpi[1])); + + final HashMap result = new HashMap(srps.size()); + BigInteger x, v; + SRP srp; + for (int i = 0; i < srps.size(); i++) + { + final String digestID = String.valueOf(i); + srp = (SRP) srps.get(digestID); + x = new BigInteger(1, srp.computeX(s, user, password)); + v = g.modPow(x, N); + final String verifier = Util.toBase64(v.toByteArray()); + + result.put(digestID, verifier); + } + return result; + } + + private synchronized void update() throws IOException + { + entries.clear(); + + FileInputStream fis; + passwdFile = new File(pwName); + lastmodPasswdFile = passwdFile.lastModified(); + try + { + fis = new FileInputStream(passwdFile); + readPasswd(fis); + } + catch (FileNotFoundException ignored) + { + } + passwd2File = new File(pw2Name); + lastmodPasswd2File = passwd2File.lastModified(); + try + { + fis = new FileInputStream(passwd2File); + readPasswd2(fis); + } + catch (FileNotFoundException ignored) + { + } + } + + private void checkCurrent() throws IOException + { + if (passwdFile.lastModified() > lastmodPasswdFile + || passwd2File.lastModified() > lastmodPasswd2File) + { + update(); + } + } + + private void readPasswd(final InputStream in) throws IOException + { + final BufferedReader din = new BufferedReader(new InputStreamReader(in)); + String line, user, verifier, salt, index; + StringTokenizer st; + while ((line = din.readLine()) != null) + { + st = new StringTokenizer(line, ":"); + try + { + user = st.nextToken(); + verifier = st.nextToken(); + salt = st.nextToken(); + index = st.nextToken(); + } + catch (NoSuchElementException x) + { + throw new IOException("SRP base password file corrupt"); + } + + final HashMap verifiers = new HashMap(6); + verifiers.put("0", verifier); + + final HashMap fields = new HashMap(4); + fields.put(USER_FIELD, user); + fields.put(VERIFIERS_FIELD, verifiers); + fields.put(SALT_FIELD, salt); + fields.put(CONFIG_FIELD, index); + + entries.put(user, fields); + } + } + + private void readPasswd2(final InputStream in) throws IOException + { + final BufferedReader din = new BufferedReader(new InputStreamReader(in)); + String line, digestID, user, verifier; + StringTokenizer st; + HashMap fields, verifiers; + while ((line = din.readLine()) != null) + { + st = new StringTokenizer(line, ":"); + try + { + digestID = st.nextToken(); + user = st.nextToken(); + verifier = st.nextToken(); + } + catch (NoSuchElementException x) + { + throw new IOException("SRP extended password file corrupt"); + } + + fields = (HashMap) entries.get(user); + if (fields != null) + { + verifiers = (HashMap) fields.get(VERIFIERS_FIELD); + verifiers.put(digestID, verifier); + } + } + } + + private void writePasswd(final PrintWriter pw1, final PrintWriter pw2) + throws IOException + { + String user, digestID; + HashMap fields, verifiers; + StringBuffer sb1, sb2; + Iterator j; + final Iterator i = entries.keySet().iterator(); + while (i.hasNext()) + { + user = (String) i.next(); + fields = (HashMap) entries.get(user); + if (!user.equals(fields.get(USER_FIELD))) + { + throw new IOException("Inconsistent SRP password data"); + } + verifiers = (HashMap) fields.get(VERIFIERS_FIELD); + sb1 = new StringBuffer().append(user).append(":").append( + (String) verifiers.get("0")).append( + ":").append( + (String) fields.get(SALT_FIELD)).append( + ":").append( + (String) fields.get(CONFIG_FIELD)); + pw1.println(sb1.toString()); + // write extended information + j = verifiers.keySet().iterator(); + while (j.hasNext()) + { + digestID = (String) j.next(); + if (!"0".equals(digestID)) + { + // #0 is the default digest, already present in tpasswd! + sb2 = new StringBuffer().append(digestID).append(":").append( + user).append( + ":").append( + (String) verifiers.get(digestID)); + pw2.println(sb2.toString()); + } + } + } + } +}
\ No newline at end of file diff --git a/libjava/classpath/gnu/javax/crypto/sasl/srp/SRP.java b/libjava/classpath/gnu/javax/crypto/sasl/srp/SRP.java new file mode 100644 index 00000000000..d3eb596d4c3 --- /dev/null +++ b/libjava/classpath/gnu/javax/crypto/sasl/srp/SRP.java @@ -0,0 +1,285 @@ +/* SRP.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a 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 of the License, 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; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, 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.javax.crypto.sasl.srp; + +import gnu.java.security.hash.HashFactory; +import gnu.java.security.hash.IMessageDigest; +import gnu.java.security.util.Util; + +import java.io.UnsupportedEncodingException; +import java.math.BigInteger; +import java.util.HashMap; + +/** + * <p>A Factory class that returns SRP Singletons that know all SRP-related + * mathematical computations and protocol-related operations for both the + * client- and server-sides.</p> + */ +public final class SRP +{ + + // Constants and variables + // -------------------------------------------------------------------------- + + /** The map of already instantiated SRP algorithm instances. */ + private static final HashMap algorithms = new HashMap(); + + private static final byte COLON = (byte) 0x3A; + + /** The underlying message digest algorithm used for all SRP calculations. */ + private IMessageDigest mda; + + // Constructor(s) + // -------------------------------------------------------------------------- + + /** Trivial private constructor to enforce Singleton pattern. */ + private SRP(final IMessageDigest mda) + { + super(); + + this.mda = mda; + } + + // Class methods + // ------------------------------------------------------------------------- + + /** + * <p>Returns an instance of this object that uses the designated message + * digest algorithm as its digest function.</p> + * + * @return an instance of this object for the designated digest name. + */ + public static synchronized SRP instance(String mdName) + { + if (mdName != null) + { + mdName = mdName.trim().toLowerCase(); + } + if (mdName == null || mdName.equals("")) + { + mdName = SRPRegistry.SRP_DEFAULT_DIGEST_NAME; + } + SRP result = (SRP) algorithms.get(mdName); + if (result == null) + { + final IMessageDigest mda = HashFactory.getInstance(mdName); + result = new SRP(mda); + algorithms.put(mdName, result); + } + return result; + } + + private static final byte[] xor(final byte[] b1, final byte[] b2, + final int length) + { + final byte[] result = new byte[length]; + for (int i = 0; i < length; ++i) + { + result[i] = (byte) (b1[i] ^ b2[i]); + } + return result; + } + + // Instance methods + // ------------------------------------------------------------------------- + + /** @return the message digest algorithm name used by this instance. */ + public String getAlgorithm() + { + return mda.name(); + } + + // Message Digest algorithm related methods -------------------------------- + + /** + * <p>Returns a new instance of the SRP message digest algorithm --which is + * SHA-160 by default, but could be anything else provided the proper + * conditions as specified in the SRP specifications.</p> + * + * @return a new instance of the underlying SRP message digest algorithm. + * @throws RuntimeException if the implementation of the message digest + * algorithm does not support cloning. + */ + public IMessageDigest newDigest() + { + return (IMessageDigest) mda.clone(); + } + + /** + * <p>Convenience method to return the result of digesting the designated + * input with a new instance of the SRP message digest algorithm.</p> + * + * @param src some bytes to digest. + * @return the bytes constituting the result of digesting the designated + * input with a new instance of the SRP message digest algorithm. + */ + public byte[] digest(final byte[] src) + { + final IMessageDigest hash = (IMessageDigest) mda.clone(); + hash.update(src, 0, src.length); + return hash.digest(); + } + + /** + * <p>Convenience method to return the result of digesting the designated + * input with a new instance of the SRP message digest algorithm.</p> + * + * @param src a String whose bytes (using US-ASCII encoding) are to be + * digested. + * @return the bytes constituting the result of digesting the designated + * input with a new instance of the SRP message digest algorithm. + * @throws UnsupportedEncodingException if US-ASCII charset is not found. + */ + public byte[] digest(final String src) throws UnsupportedEncodingException + { + return digest(src.getBytes("US-ASCII")); + } + + // Other methods ----------------------------------------------------------- + + /** + * <p>Convenience method to XOR N bytes from two arrays; N being the output + * size of the SRP message digest algorithm.</p> + * + * @param a the first byte array. + * @param b the second one. + * @return N bytes which are the result of the XOR operations on the first N + * bytes from the designated arrays. N is the size of the SRP message digest + * algorithm; eg. 20 for SHA-160. + */ + public byte[] xor(final byte[] a, final byte[] b) + { + return xor(a, b, mda.hashSize()); + } + + public byte[] generateM1(final BigInteger N, final BigInteger g, + final String U, final byte[] s, final BigInteger A, + final BigInteger B, final byte[] K, final String I, + final String L, final byte[] cn, final byte[] cCB) + throws UnsupportedEncodingException + { + final IMessageDigest hash = (IMessageDigest) mda.clone(); + byte[] b; + b = xor(digest(Util.trim(N)), digest(Util.trim(g))); + hash.update(b, 0, b.length); + b = digest(U); + hash.update(b, 0, b.length); + hash.update(s, 0, s.length); + b = Util.trim(A); + hash.update(b, 0, b.length); + b = Util.trim(B); + hash.update(b, 0, b.length); + hash.update(K, 0, K.length); + b = digest(I); + hash.update(b, 0, b.length); + b = digest(L); + hash.update(b, 0, b.length); + hash.update(cn, 0, cn.length); + hash.update(cCB, 0, cCB.length); + + return hash.digest(); + } + + public byte[] generateM2(final BigInteger A, final byte[] M1, final byte[] K, + final String U, final String I, final String o, + final byte[] sid, final int ttl, final byte[] cIV, + final byte[] sIV, final byte[] sCB) + throws UnsupportedEncodingException + { + final IMessageDigest hash = (IMessageDigest) mda.clone(); + byte[] b; + b = Util.trim(A); + hash.update(b, 0, b.length); + hash.update(M1, 0, M1.length); + hash.update(K, 0, K.length); + b = digest(U); + hash.update(b, 0, b.length); + b = digest(I); + hash.update(b, 0, b.length); + b = digest(o); + hash.update(b, 0, b.length); + hash.update(sid, 0, sid.length); + hash.update((byte) (ttl >>> 24)); + hash.update((byte) (ttl >>> 16)); + hash.update((byte) (ttl >>> 8)); + hash.update((byte) ttl); + hash.update(cIV, 0, cIV.length); + hash.update(sIV, 0, sIV.length); + hash.update(sCB, 0, sCB.length); + + return hash.digest(); + } + + public byte[] generateKn(final byte[] K, final byte[] cn, final byte[] sn) + { + final IMessageDigest hash = (IMessageDigest) mda.clone(); + hash.update(K, 0, K.length); + hash.update(cn, 0, cn.length); + hash.update(sn, 0, sn.length); + + return hash.digest(); + } + + public byte[] computeX(final byte[] s, final String user, + final String password) + throws UnsupportedEncodingException + { + return computeX(s, user.getBytes("US-ASCII"), password.getBytes("US-ASCII")); + } + + public byte[] computeX(final byte[] s, final String user, final byte[] p) + throws UnsupportedEncodingException + { + return computeX(s, user.getBytes("US-ASCII"), p); + } + + private byte[] computeX(final byte[] s, final byte[] user, final byte[] p) + { + final IMessageDigest hash = (IMessageDigest) mda.clone(); + hash.update(user, 0, user.length); + hash.update(COLON); + hash.update(p, 0, p.length); + final byte[] up = hash.digest(); + + hash.update(s, 0, s.length); + hash.update(up, 0, up.length); + + return hash.digest(); + } +}
\ No newline at end of file diff --git a/libjava/classpath/gnu/javax/crypto/sasl/srp/SRPAuthInfoProvider.java b/libjava/classpath/gnu/javax/crypto/sasl/srp/SRPAuthInfoProvider.java new file mode 100644 index 00000000000..9ea21efb6c2 --- /dev/null +++ b/libjava/classpath/gnu/javax/crypto/sasl/srp/SRPAuthInfoProvider.java @@ -0,0 +1,218 @@ +/* SRPAuthInfoProvider.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a 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 of the License, 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; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, 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.javax.crypto.sasl.srp; + +import gnu.java.security.Registry; +import gnu.java.security.util.Util; +import gnu.javax.crypto.sasl.IAuthInfoProvider; +import gnu.javax.crypto.sasl.NoSuchUserException; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import javax.security.sasl.AuthenticationException; + +/** + * <p>The SRP mechanism authentication information provider implementation.</p> + */ +public class SRPAuthInfoProvider implements IAuthInfoProvider +{ + + // Constants and variables + // ------------------------------------------------------------------------- + + private PasswordFile passwordFile = null; + + // Constructor(s) + // ------------------------------------------------------------------------- + + // implicit 0-args constrcutor + + // Class methods + // ------------------------------------------------------------------------- + + // Instance methods + // ------------------------------------------------------------------------- + + // IAuthInfoProvider interface implementation ------------------------------ + + public void activate(Map context) throws AuthenticationException + { + try + { + if (context == null) + { + passwordFile = new PasswordFile(); + } + else + { + passwordFile = (PasswordFile) context.get(SRPRegistry.PASSWORD_DB); + if (passwordFile == null) + { + String pfn = (String) context.get(SRPRegistry.PASSWORD_FILE); + if (pfn == null) + { + passwordFile = new PasswordFile(); + } + else + { + passwordFile = new PasswordFile(pfn); + } + } + } + } + catch (IOException x) + { + throw new AuthenticationException("activate()", x); + } + } + + public void passivate() throws AuthenticationException + { + passwordFile = null; + } + + public boolean contains(String userName) throws AuthenticationException + { + if (passwordFile == null) + { + throw new AuthenticationException("contains()", + new IllegalStateException()); + } + boolean result = false; + try + { + result = passwordFile.contains(userName); + } + catch (IOException x) + { + throw new AuthenticationException("contains()", x); + } + return result; + } + + public Map lookup(Map userID) throws AuthenticationException + { + if (passwordFile == null) + { + throw new AuthenticationException("lookup()", + new IllegalStateException()); + } + Map result = new HashMap(); + try + { + String userName = (String) userID.get(Registry.SASL_USERNAME); + if (userName == null) + { + throw new NoSuchUserException(""); + } + String mdName = (String) userID.get(SRPRegistry.MD_NAME_FIELD); + + String[] data = passwordFile.lookup(userName, mdName); + result.put(SRPRegistry.USER_VERIFIER_FIELD, data[0]); + result.put(SRPRegistry.SALT_FIELD, data[1]); + result.put(SRPRegistry.CONFIG_NDX_FIELD, data[2]); + } + catch (Exception x) + { + if (x instanceof AuthenticationException) + { + throw (AuthenticationException) x; + } + throw new AuthenticationException("lookup()", x); + } + return result; + } + + public void update(Map userCredentials) throws AuthenticationException + { + if (passwordFile == null) + throw new AuthenticationException("update()", new IllegalStateException()); + + try + { + String userName = (String) userCredentials.get(Registry.SASL_USERNAME); + String password = (String) userCredentials.get(Registry.SASL_PASSWORD); + String salt = (String) userCredentials.get(SRPRegistry.SALT_FIELD); + String config = (String) userCredentials.get(SRPRegistry.CONFIG_NDX_FIELD); + if (salt == null || config == null) + { + passwordFile.changePasswd(userName, password); + } + else + { + passwordFile.add(userName, password, Util.fromBase64(salt), config); + } + } + catch (Exception x) + { + if (x instanceof AuthenticationException) + { + throw (AuthenticationException) x; + } + throw new AuthenticationException("update()", x); + } + } + + public Map getConfiguration(String mode) throws AuthenticationException + { + if (passwordFile == null) + { + throw new AuthenticationException("getConfiguration()", + new IllegalStateException()); + } + Map result = new HashMap(); + try + { + String[] data = passwordFile.lookupConfig(mode); + result.put(SRPRegistry.SHARED_MODULUS, data[0]); + result.put(SRPRegistry.FIELD_GENERATOR, data[1]); + } + catch (Exception x) + { + if (x instanceof AuthenticationException) + { + throw (AuthenticationException) x; + } + throw new AuthenticationException("getConfiguration()", x); + } + return result; + } +}
\ No newline at end of file diff --git a/libjava/classpath/gnu/javax/crypto/sasl/srp/SRPClient.java b/libjava/classpath/gnu/javax/crypto/sasl/srp/SRPClient.java new file mode 100644 index 00000000000..1a1664ff79d --- /dev/null +++ b/libjava/classpath/gnu/javax/crypto/sasl/srp/SRPClient.java @@ -0,0 +1,1211 @@ +/* SRPClient.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a 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 of the License, 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; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, 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.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; +import gnu.javax.crypto.key.IncomingMessage; +import gnu.javax.crypto.key.KeyAgreementFactory; +import gnu.javax.crypto.key.KeyAgreementException; +import gnu.javax.crypto.key.OutgoingMessage; +import gnu.javax.crypto.key.srp6.SRP6KeyAgreement; +import gnu.javax.crypto.assembly.Direction; +import gnu.javax.crypto.cipher.CipherFactory; +import gnu.javax.crypto.cipher.IBlockCipher; +import gnu.javax.crypto.sasl.ClientMechanism; +import gnu.javax.crypto.sasl.IllegalMechanismStateException; +import gnu.javax.crypto.sasl.InputBuffer; +import gnu.javax.crypto.sasl.IntegrityException; +import gnu.javax.crypto.sasl.OutputBuffer; + +import gnu.javax.security.auth.Password; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.ByteArrayOutputStream; +import java.io.UnsupportedEncodingException; +import java.math.BigInteger; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.StringTokenizer; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.auth.DestroyFailedException; +import javax.security.sasl.AuthenticationException; +import javax.security.sasl.SaslClient; +import javax.security.sasl.SaslException; + +/** + * <p>The SASL-SRP client-side mechanism.</p> + */ +public class SRPClient extends ClientMechanism implements SaslClient +{ + + // Debugging methods and variables + // ------------------------------------------------------------------------- + + private static final String NAME = "SRPClient"; + + // private static final String ERROR = "ERROR"; + // private static final String WARN = " WARN"; + private static final String INFO = " INFO"; + + private static final String TRACE = "DEBUG"; + + private static final boolean DEBUG = true; + + private static final int debuglevel = 3; + + private static final PrintWriter err = new PrintWriter(System.out, true); + + private static void debug(final String level, final Object obj) + { + err.println("[" + level + "] " + NAME + ": " + String.valueOf(obj)); + } + + // Constants and variables + // ------------------------------------------------------------------------- + + // private static final HashMap uid2ctx = new HashMap(); + + private String uid; // the unique key for this type of client + + private String U; // the authentication identity + + BigInteger N, g, A, B; + + private Password password; // the authentication credentials + + private byte[] s; // the user's salt + + private byte[] cIV, sIV; // client+server IVs, when confidentiality is on + + private byte[] M1, M2; // client+server evidences + + private byte[] cn, sn; // client's and server's nonce + + private SRP srp; // SRP algorithm instance used by this client + + private byte[] sid; // session ID when re-used + + private int ttl; // session time-to-live in seconds + + private byte[] sCB; // the peer's channel binding data + + private String L; // available options + + private String o; + + private String chosenIntegrityAlgorithm; + + private String chosenConfidentialityAlgorithm; + + private int rawSendSize = Registry.SASL_BUFFER_MAX_LIMIT; + + private byte[] K; // shared session key + + private boolean replayDetection = true; // whether Replay Detection is on + + private int inCounter = 0; // messages sequence numbers + + private int outCounter = 0; + + private IALG inMac, outMac; // if !null, use for integrity + + private CALG inCipher, outCipher; // if !null, use for confidentiality + + private IKeyAgreementParty clientHandler = KeyAgreementFactory.getPartyAInstance(Registry.SRP_SASL_KA); + + /** Our default source of randomness. */ + private PRNG prng = null; + + // Constructor(s) + // ------------------------------------------------------------------------- + + public SRPClient() + { + super(Registry.SASL_SRP_MECHANISM); + } + + // Class methods + // ------------------------------------------------------------------------- + + // Instance methods + // ------------------------------------------------------------------------- + + // abstract methods implementation ----------------------------------------- + + protected void initMechanism() throws SaslException + { + // we shall keep track of the sid (and the security context of this + // SRP client) based on the initialisation parameters of an SRP session. + // we shall compute a unique key for those parameters and key the sid + // (and the security context) accordingly. + // 1. compute the mapping key. use MD5 (the fastest) for this purpose + final MD5 md = new MD5(); + byte[] b; + b = authorizationID.getBytes(); + md.update(b, 0, b.length); + b = serverName.getBytes(); + md.update(b, 0, b.length); + b = protocol.getBytes(); + md.update(b, 0, b.length); + if (channelBinding.length > 0) + { + md.update(channelBinding, 0, channelBinding.length); + } + uid = Util.toBase64(md.digest()); + if (ClientStore.instance().isAlive(uid)) + { + final SecurityContext ctx = ClientStore.instance().restoreSession(uid); + srp = SRP.instance(ctx.getMdName()); + sid = ctx.getSID(); + K = ctx.getK(); + cIV = ctx.getClientIV(); + sIV = ctx.getServerIV(); + replayDetection = ctx.hasReplayDetection(); + inCounter = ctx.getInCounter(); + outCounter = ctx.getOutCounter(); + inMac = ctx.getInMac(); + outMac = ctx.getOutMac(); + inCipher = ctx.getInCipher(); + outCipher = ctx.getOutCipher(); + } + else + { + sid = new byte[0]; + ttl = 0; + K = null; + cIV = null; + sIV = null; + cn = null; + sn = null; + } + } + + protected void resetMechanism() throws SaslException + { + try + { + password.destroy(); + } + catch (DestroyFailedException dfe) + { + SaslException se = new SaslException("resetMechanism()"); + se.initCause(dfe); + throw se; + } + password = null; + M1 = null; + K = null; + cIV = null; + sIV = null; + inMac = outMac = null; + inCipher = outCipher = null; + + sid = null; + ttl = 0; + cn = null; + sn = null; + } + + // javax.security.sasl.SaslClient interface implementation ----------------- + + public boolean hasInitialResponse() + { + return true; + } + + public byte[] evaluateChallenge(final byte[] challenge) throws SaslException + { + switch (state) + { + case 0: + state++; + return sendIdentities(); + case 1: + state++; + final byte[] result = sendPublicKey(challenge); + try + { + password.destroy(); //don't need further this session + } + catch (DestroyFailedException x) + { + SaslException se = new SaslException("sendPublicKey()"); + se.initCause(se); + throw se; + } + return result; + case 2: // should only occur if session re-use was rejected + if (!complete) + { + state++; + return receiveEvidence(challenge); + } + // else fall through + default: + throw new IllegalMechanismStateException("evaluateChallenge()"); + } + } + + protected byte[] engineUnwrap(final byte[] incoming, final int offset, + final int len) throws SaslException + { + if (DEBUG && debuglevel > 8) + debug(TRACE, "==> engineUnwrap()"); + + if (inMac == null && inCipher == null) + { + throw new IllegalStateException("connection is not protected"); + } + + // at this point one, or both, of confidentiality and integrity protection + // services are active. + + final byte[] result; + try + { + // final InputBuffer frameIn = InputBuffer.getInstance(incoming, offset, len); + // result = frameIn.getEOS(); + if (inMac != null) + { // integrity bytes are at the end of the stream + final int macBytesCount = inMac.length(); + final int payloadLength = len - macBytesCount; + // final byte[] received_mac = frameIn.getOS(); + final byte[] received_mac = new byte[macBytesCount]; + System.arraycopy(incoming, offset + payloadLength, received_mac, 0, + macBytesCount); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Got C (received MAC): " + + Util.dumpString(received_mac)); + // inMac.update(result); + inMac.update(incoming, offset, payloadLength); + if (replayDetection) + { + inCounter++; + if (DEBUG && debuglevel > 6) + debug(TRACE, "inCounter=" + String.valueOf(inCounter)); + inMac.update(new byte[] { (byte) (inCounter >>> 24), + (byte) (inCounter >>> 16), + (byte) (inCounter >>> 8), + (byte) inCounter }); + } + + final byte[] computed_mac = inMac.doFinal(); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Computed MAC: " + Util.dumpString(computed_mac)); + if (!Arrays.equals(received_mac, computed_mac)) + { + throw new IntegrityException("engineUnwrap()"); + } + + // deal with the payload, which can be either plain or encrypted + if (inCipher != null) + { + result = inCipher.doFinal(incoming, offset, payloadLength); + } + else + { + result = new byte[len - macBytesCount]; + System.arraycopy(incoming, offset, result, 0, result.length); + } + } + else + { // no integrity protection; just confidentiality + // if (inCipher != null) { + result = inCipher.doFinal(incoming, offset, len); + // } else { + // result = new byte[len]; + // System.arraycopy(incoming, offset, result, 0, len); + // } + } + // if (inCipher != null) { + // result = inCipher.doFinal(result); + // } + } + catch (IOException x) + { + if (x instanceof SaslException) + { + throw (SaslException) x; + } + throw new SaslException("engineUnwrap()", x); + } + + if (DEBUG && debuglevel > 8) + debug(TRACE, "<== engineUnwrap()"); + return result; + } + + protected byte[] engineWrap(final byte[] outgoing, final int offset, + final int len) throws SaslException + { + if (DEBUG && debuglevel > 8) + debug(TRACE, "==> engineWrap()"); + + if (outMac == null && outCipher == null) + { + throw new IllegalStateException("connection is not protected"); + } + + // at this point one, or both, of confidentiality and integrity protection + // services are active. + + // byte[] data = new byte[len]; + // System.arraycopy(outgoing, offset, data, 0, len); + byte[] result; + try + { + // OutputBuffer frameOut = new OutputBuffer(); + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + // Process the data + if (outCipher != null) + { + // data = outCipher.doFinal(data); + result = outCipher.doFinal(outgoing, offset, len); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Encoding c (encrypted plaintext): " + + Util.dumpString(result)); + + // frameOut.setEOS(data); + out.write(result); + + if (outMac != null) + { + outMac.update(result); + if (replayDetection) + { + outCounter++; + if (DEBUG && debuglevel > 6) + debug(TRACE, "outCounter=" + String.valueOf(outCounter)); + outMac.update(new byte[] { (byte) (outCounter >>> 24), + (byte) (outCounter >>> 16), + (byte) (outCounter >>> 8), + (byte) outCounter }); + } + final byte[] C = outMac.doFinal(); + // frameOut.setOS(C); + out.write(C); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Encoding C (integrity checksum): " + + Util.dumpString(C)); + } // else confidentiality only; do nothing + } + else + { // no confidentiality; just integrity [+ replay detection] + // if (DEBUG && debuglevel > 6) debug(TRACE, "Encoding p (plaintext): "+Util.dumpString(data)); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Encoding p (plaintext): " + + Util.dumpString(outgoing, offset, len)); + + // frameOut.setEOS(data); + out.write(outgoing, offset, len); + + // if (outMac != null) { + // outMac.update(data); + outMac.update(outgoing, offset, len); + if (replayDetection) + { + outCounter++; + if (DEBUG && debuglevel > 6) + debug(TRACE, "outCounter=" + String.valueOf(outCounter)); + outMac.update(new byte[] { (byte) (outCounter >>> 24), + (byte) (outCounter >>> 16), + (byte) (outCounter >>> 8), + (byte) outCounter }); + } + final byte[] C = outMac.doFinal(); + // frameOut.setOS(C); + out.write(C); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Encoding C (integrity checksum): " + + Util.dumpString(C)); + // } + } + + // frameOut.setEOS(data); + // + // if (outMac != null) { + // outMac.update(data); + // if (replayDetection) { + // outCounter++; + // if (DEBUG && debuglevel > 6) debug(TRACE, "outCounter="+String.valueOf(outCounter)); + // outMac.update(new byte[] { + // (byte)(outCounter >>> 24), + // (byte)(outCounter >>> 16), + // (byte)(outCounter >>> 8), + // (byte) outCounter }); + // } + // byte[] C = outMac.doFinal(); + // frameOut.setOS(C); + // if (DEBUG && debuglevel > 6) debug(TRACE, "Encoding C (integrity checksum): "+Util.dumpString(C)); + // } + + // result = frameOut.wrap(); + result = out.toByteArray(); + + } + catch (IOException x) + { + if (x instanceof SaslException) + { + throw (SaslException) x; + } + throw new SaslException("engineWrap()", x); + } + + if (DEBUG && debuglevel > 8) + debug(TRACE, "<== engineWrap()"); + return result; + } + + protected String getNegotiatedQOP() + { + if (inMac != null) + { + if (inCipher != null) + { + return Registry.QOP_AUTH_CONF; + } + else + { + return Registry.QOP_AUTH_INT; + } + } + return Registry.QOP_AUTH; + } + + protected String getNegotiatedStrength() + { + if (inMac != null) + { + if (inCipher != null) + { + return Registry.STRENGTH_HIGH; + } + else + { + return Registry.STRENGTH_MEDIUM; + } + } + return Registry.STRENGTH_LOW; + } + + protected String getNegotiatedRawSendSize() + { + return String.valueOf(rawSendSize); + } + + protected String getReuse() + { + return Registry.REUSE_TRUE; + } + + // other methods ----------------------------------------------------------- + + private byte[] sendIdentities() throws SaslException + { + if (DEBUG && debuglevel > 8) + debug(TRACE, "==> sendIdentities()"); + + // If necessary, prompt the client for the username and password + getUsernameAndPassword(); + + if (DEBUG && debuglevel > 6) + debug(TRACE, "Password: \"" + new String(password.getPassword()) + "\""); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Encoding U (username): \"" + U + "\""); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Encoding I (userid): \"" + authorizationID + "\""); + + // if session re-use generate new 16-byte nonce + if (sid.length != 0) + { + cn = new byte[16]; + getDefaultPRNG().nextBytes(cn); + } + else + { + cn = new byte[0]; + } + + final OutputBuffer frameOut = new OutputBuffer(); + try + { + frameOut.setText(U); + frameOut.setText(authorizationID); + frameOut.setEOS(sid); // session ID to re-use + frameOut.setOS(cn); // client nonce + frameOut.setEOS(channelBinding); + } + catch (IOException x) + { + if (x instanceof SaslException) + { + throw (SaslException) x; + } + throw new AuthenticationException("sendIdentities()", x); + } + final byte[] result = frameOut.encode(); + if (DEBUG && debuglevel > 8) + debug(TRACE, "<== sendIdentities()"); + if (DEBUG && debuglevel > 2) + debug(INFO, "C: " + Util.dumpString(result)); + if (DEBUG && debuglevel > 2) + debug(INFO, " U = " + U); + if (DEBUG && debuglevel > 2) + debug(INFO, " I = " + authorizationID); + if (DEBUG && debuglevel > 2) + debug(INFO, "sid = " + new String(sid)); + if (DEBUG && debuglevel > 2) + debug(INFO, " cn = " + Util.dumpString(cn)); + if (DEBUG && debuglevel > 2) + debug(INFO, "cCB = " + Util.dumpString(channelBinding)); + return result; + } + + private byte[] sendPublicKey(final byte[] input) throws SaslException + { + if (DEBUG && debuglevel > 8) + debug(TRACE, "==> sendPublicKey()"); + if (DEBUG && debuglevel > 6) + debug(TRACE, "S: " + Util.dumpString(input)); + + // Server sends [00], N, g, s, B, L + // or [FF], sn, sCB + final InputBuffer frameIn = new InputBuffer(input); + final int ack; + try + { + ack = (int) frameIn.getScalar(1); + if (ack == 0x00) + { // new session + N = frameIn.getMPI(); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Got N (modulus): " + Util.dump(N)); + g = frameIn.getMPI(); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Got g (generator): " + Util.dump(g)); + s = frameIn.getOS(); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Got s (salt): " + Util.dumpString(s)); + B = frameIn.getMPI(); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Got B (server ephermeral public key): " + + Util.dump(B)); + L = frameIn.getText(); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Got L (available options): \"" + L + "\""); + } + else if (ack == 0xFF) + { // session re-use + sn = frameIn.getOS(); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Got sn (server nonce): " + Util.dumpString(sn)); + sCB = frameIn.getEOS(); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Got sCB (server channel binding): " + + Util.dumpString(sCB)); + } + else + { // unexpected scalar + throw new SaslException("sendPublicKey(): Invalid scalar (" + ack + + ") in server's request"); + } + } + catch (IOException x) + { + if (x instanceof SaslException) + { + throw (SaslException) x; + } + throw new SaslException("sendPublicKey()", x); + } + + if (ack == 0x00) + { // new session --------------------------------------- + o = createO(L.toLowerCase()); // do this first to initialise the SRP hash + + final byte[] pBytes; // use ASCII encoding to inter-operate w/ non-java + pBytes = password.getBytes(); + + // ---------------------------------------------------------------------- + final HashMap mapA = new HashMap(); + // mapA.put(SRP6KeyAgreement.HASH_FUNCTION, srp.newDigest()); + mapA.put(SRP6KeyAgreement.HASH_FUNCTION, srp.getAlgorithm()); + mapA.put(SRP6KeyAgreement.USER_IDENTITY, U); + mapA.put(SRP6KeyAgreement.USER_PASSWORD, pBytes); + try + { + clientHandler.init(mapA); + clientHandler.processMessage(null); + } + catch (KeyAgreementException x) + { + throw new SaslException("sendPublicKey()", x); + } + + // ---------------------------------------------------------------------- + + // ------------------------------------------------------------------- + try + { + OutgoingMessage out = new OutgoingMessage(); + out.writeMPI(N); + out.writeMPI(g); + out.writeMPI(new BigInteger(1, s)); + out.writeMPI(B); + IncomingMessage in = new IncomingMessage(out.toByteArray()); + out = clientHandler.processMessage(in); + + in = new IncomingMessage(out.toByteArray()); + A = in.readMPI(); + K = clientHandler.getSharedSecret(); + } + catch (KeyAgreementException x) + { + throw new SaslException("sendPublicKey()", x); + } + // ------------------------------------------------------------------- + + if (DEBUG && debuglevel > 6) + debug(TRACE, "K: " + Util.dumpString(K)); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Encoding A (client ephemeral public key): " + + Util.dump(A)); + + try + { + M1 = srp.generateM1(N, g, U, s, A, B, K, authorizationID, L, cn, + channelBinding); + } + catch (UnsupportedEncodingException x) + { + throw new AuthenticationException("sendPublicKey()", x); + } + + if (DEBUG && debuglevel > 6) + debug(TRACE, "Encoding o (client chosen options): \"" + o + "\""); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Encoding cIV (client IV): \"" + Util.dumpString(cIV) + + "\""); + + final OutputBuffer frameOut = new OutputBuffer(); + try + { + frameOut.setMPI(A); + frameOut.setOS(M1); + frameOut.setText(o); + frameOut.setOS(cIV); + } + catch (IOException x) + { + if (x instanceof SaslException) + { + throw (SaslException) x; + } + throw new AuthenticationException("sendPublicKey()", x); + } + final byte[] result = frameOut.encode(); + if (DEBUG && debuglevel > 8) + debug(TRACE, "<== sendPublicKey()"); + if (DEBUG && debuglevel > 2) + debug(INFO, "New session, or session re-use rejected..."); + if (DEBUG && debuglevel > 2) + debug(INFO, "C: " + Util.dumpString(result)); + if (DEBUG && debuglevel > 2) + debug(INFO, " A = 0x" + A.toString(16)); + if (DEBUG && debuglevel > 2) + debug(INFO, " M1 = " + Util.dumpString(M1)); + if (DEBUG && debuglevel > 2) + debug(INFO, " o = " + o); + if (DEBUG && debuglevel > 2) + debug(INFO, "cIV = " + Util.dumpString(cIV)); + + return result; + } + else + { // session re-use accepted ------------------------------------- + setupSecurityServices(true); + if (DEBUG && debuglevel > 8) + debug(TRACE, "<== sendPublicKey()"); + if (DEBUG && debuglevel > 2) + debug(INFO, "Session re-use accepted..."); + return null; + } + } + + private byte[] receiveEvidence(byte[] input) throws SaslException + { + if (DEBUG && debuglevel > 8) + debug(TRACE, "==> receiveEvidence()"); + if (DEBUG && debuglevel > 6) + debug(TRACE, "S: " + Util.dumpString(input)); + + // Server send M2, sIV, sCB, sid, ttl + final InputBuffer frameIn = new InputBuffer(input); + try + { + M2 = frameIn.getOS(); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Got M2 (server evidence): " + Util.dumpString(M2)); + sIV = frameIn.getOS(); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Got sIV (server IV): " + Util.dumpString(sIV)); + sid = frameIn.getEOS(); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Got sid (session ID): " + new String(sid)); + ttl = (int) frameIn.getScalar(4); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Got ttl (session time-to-live): " + ttl + "sec."); + sCB = frameIn.getEOS(); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Got sCB (server channel binding): " + + Util.dumpString(sCB)); + } + catch (IOException x) + { + if (x instanceof SaslException) + { + throw (SaslException) x; + } + throw new AuthenticationException("receiveEvidence()", x); + } + + final byte[] expected; + try + { + expected = srp.generateM2(A, M1, K, U, authorizationID, o, sid, ttl, + cIV, sIV, sCB); + } + catch (UnsupportedEncodingException x) + { + throw new AuthenticationException("receiveEvidence()", x); + } + + if (DEBUG && debuglevel > 6) + debug(TRACE, "Expected: " + Util.dumpString(expected)); + if (!Arrays.equals(M2, expected)) + { + throw new AuthenticationException("M2 mismatch"); + } + + setupSecurityServices(false); + + if (DEBUG && debuglevel > 8) + debug(TRACE, "<== receiveEvidence()"); + return null; + } + + private void getUsernameAndPassword() throws AuthenticationException + { + try + { + if ((!properties.containsKey(Registry.SASL_USERNAME)) + && (!properties.containsKey(Registry.SASL_PASSWORD))) + { + final NameCallback nameCB; + final String defaultName = System.getProperty("user.name"); + if (defaultName == null) + { + nameCB = new NameCallback("username: "); + } + else + { + nameCB = new NameCallback("username: ", defaultName); + } + final PasswordCallback pwdCB = new PasswordCallback("password: ", + false); + handler.handle(new Callback[] { nameCB, pwdCB }); + U = nameCB.getName(); + password = new Password(pwdCB.getPassword()); + } + else + { + if (properties.containsKey(Registry.SASL_USERNAME)) + { + this.U = (String) properties.get(Registry.SASL_USERNAME); + } + else + { + final NameCallback nameCB; + final String defaultName = System.getProperty("user.name"); + if (defaultName == null) + { + nameCB = new NameCallback("username: "); + } + else + { + nameCB = new NameCallback("username: ", defaultName); + } + this.handler.handle(new Callback[] { nameCB }); + this.U = nameCB.getName(); + } + + if (properties.containsKey(Registry.SASL_PASSWORD)) + { + Object pw = properties.get(Registry.SASL_PASSWORD); + if (pw instanceof char[]) + password = new Password((char[]) pw); + else if (pw instanceof Password) + password = (Password) pw; + else if (pw instanceof String) + password = new Password(((String) pw).toCharArray()); + else + throw new IllegalArgumentException( + pw.getClass().getName() + + "is not a valid password class"); + } + else + { + final PasswordCallback pwdCB = new PasswordCallback( + "password: ", + false); + this.handler.handle(new Callback[] { pwdCB }); + password = new Password(pwdCB.getPassword()); + } + } + + if (U == null) + { + throw new AuthenticationException("null username supplied"); + } + if (password == null) + { + throw new AuthenticationException("null password supplied"); + } + } + catch (UnsupportedCallbackException x) + { + throw new AuthenticationException("getUsernameAndPassword()", x); + } + catch (IOException x) + { + throw new AuthenticationException("getUsernameAndPassword()", x); + } + } + + // We go through the list of available services and for each available one + // we decide whether or not we want it enabled, based on properties passed + // to us by the client. + private String createO(final String aol) throws AuthenticationException + { + if (DEBUG && debuglevel > 8) + debug(TRACE, "==> createO(\"" + aol + "\")"); + + boolean replaydetectionAvailable = false; + boolean integrityAvailable = false; + boolean confidentialityAvailable = false; + String option, mandatory = SRPRegistry.DEFAULT_MANDATORY; + int i; + + String mdName = SRPRegistry.SRP_DEFAULT_DIGEST_NAME; + + final StringTokenizer st = new StringTokenizer(aol, ","); + while (st.hasMoreTokens()) + { + option = st.nextToken(); + if (option.startsWith(SRPRegistry.OPTION_SRP_DIGEST + "=")) + { + option = option.substring(option.indexOf('=') + 1); + if (DEBUG && debuglevel > 6) + debug(TRACE, "mda: <" + option + ">"); + for (i = 0; i < SRPRegistry.INTEGRITY_ALGORITHMS.length; i++) + { + if (SRPRegistry.SRP_ALGORITHMS[i].equals(option)) + { + mdName = option; + break; + } + } + } + else if (option.equals(SRPRegistry.OPTION_REPLAY_DETECTION)) + { + replaydetectionAvailable = true; + } + else if (option.startsWith(SRPRegistry.OPTION_INTEGRITY + "=")) + { + option = option.substring(option.indexOf('=') + 1); + if (DEBUG && debuglevel > 6) + debug(TRACE, "ialg: <" + option + ">"); + for (i = 0; i < SRPRegistry.INTEGRITY_ALGORITHMS.length; i++) + { + if (SRPRegistry.INTEGRITY_ALGORITHMS[i].equals(option)) + { + chosenIntegrityAlgorithm = option; + integrityAvailable = true; + break; + } + } + } + else if (option.startsWith(SRPRegistry.OPTION_CONFIDENTIALITY + "=")) + { + option = option.substring(option.indexOf('=') + 1); + if (DEBUG && debuglevel > 6) + debug(TRACE, "calg: <" + option + ">"); + for (i = 0; i < SRPRegistry.CONFIDENTIALITY_ALGORITHMS.length; i++) + { + if (SRPRegistry.CONFIDENTIALITY_ALGORITHMS[i].equals(option)) + { + chosenConfidentialityAlgorithm = option; + confidentialityAvailable = true; + break; + } + } + } + else if (option.startsWith(SRPRegistry.OPTION_MANDATORY + "=")) + { + mandatory = option.substring(option.indexOf('=') + 1); + } + else if (option.startsWith(SRPRegistry.OPTION_MAX_BUFFER_SIZE + "=")) + { + final String maxBufferSize = option.substring(option.indexOf('=') + 1); + try + { + rawSendSize = Integer.parseInt(maxBufferSize); + if (rawSendSize > Registry.SASL_BUFFER_MAX_LIMIT + || rawSendSize < 1) + { + throw new AuthenticationException( + "Illegal value for 'maxbuffersize' option"); + } + } + catch (NumberFormatException x) + { + throw new AuthenticationException( + SRPRegistry.OPTION_MAX_BUFFER_SIZE + + "=" + + String.valueOf(maxBufferSize), + x); + } + } + } + + replayDetection = replaydetectionAvailable + && Boolean.valueOf( + (String) properties.get(SRPRegistry.SRP_REPLAY_DETECTION)).booleanValue(); + boolean integrity = integrityAvailable + && Boolean.valueOf( + (String) properties.get(SRPRegistry.SRP_INTEGRITY_PROTECTION)).booleanValue(); + boolean confidentiality = confidentialityAvailable + && Boolean.valueOf( + (String) properties.get(SRPRegistry.SRP_CONFIDENTIALITY)).booleanValue(); + + // make sure we do the right thing + if (SRPRegistry.OPTION_REPLAY_DETECTION.equals(mandatory)) + { + replayDetection = true; + integrity = true; + } + else if (SRPRegistry.OPTION_INTEGRITY.equals(mandatory)) + { + integrity = true; + } + else if (SRPRegistry.OPTION_CONFIDENTIALITY.equals(mandatory)) + { + confidentiality = true; + } + if (replayDetection) + { + if (chosenIntegrityAlgorithm == null) + { + throw new AuthenticationException( + "Replay detection is required but no " + + "integrity protection algorithm was chosen"); + } + } + if (integrity) + { + if (chosenIntegrityAlgorithm == null) + { + throw new AuthenticationException( + "Integrity protection is required but no " + + "algorithm was chosen"); + } + } + if (confidentiality) + { + if (chosenConfidentialityAlgorithm == null) + { + throw new AuthenticationException( + "Confidentiality protection is required " + + "but no algorithm was chosen"); + } + } + + // 1. check if we'll be using confidentiality; if not set IV to 0-byte + if (chosenConfidentialityAlgorithm == null) + { + cIV = new byte[0]; + } + else + { + // 2. get the block size of the cipher + final IBlockCipher cipher = CipherFactory.getInstance(chosenConfidentialityAlgorithm); + if (cipher == null) + { + throw new AuthenticationException("createO()", + new NoSuchAlgorithmException()); + } + final int blockSize = cipher.defaultBlockSize(); + // 3. generate random iv + cIV = new byte[blockSize]; + getDefaultPRNG().nextBytes(cIV); + } + + srp = SRP.instance(mdName); + + // Now create the options list specifying which of the available options + // we have chosen. + + // For now we just select the defaults. Later we need to add support for + // properties (perhaps in a file) where a user can specify the list of + // algorithms they would prefer to use. + + final StringBuffer sb = new StringBuffer(); + sb.append(SRPRegistry.OPTION_SRP_DIGEST).append("=").append(mdName).append( + ","); + if (replayDetection) + { + sb.append(SRPRegistry.OPTION_REPLAY_DETECTION).append(","); + } + if (integrity) + { + sb.append(SRPRegistry.OPTION_INTEGRITY).append("=").append( + chosenIntegrityAlgorithm).append( + ","); + } + if (confidentiality) + { + sb.append(SRPRegistry.OPTION_CONFIDENTIALITY).append("=").append( + chosenConfidentialityAlgorithm).append( + ","); + } + final String result = sb.append(SRPRegistry.OPTION_MAX_BUFFER_SIZE).append( + "=").append( + Registry.SASL_BUFFER_MAX_LIMIT).toString(); + + if (DEBUG && debuglevel > 8) + debug(TRACE, "<== createO() --> " + result); + return result; + } + + private void setupSecurityServices(final boolean sessionReUse) + throws SaslException + { + complete = true; // signal end of authentication phase + if (!sessionReUse) + { + outCounter = inCounter = 0; + // instantiate cipher if confidentiality protection filter is active + if (chosenConfidentialityAlgorithm != null) + { + if (DEBUG && debuglevel > 2) + debug(INFO, "Activating confidentiality protection filter"); + inCipher = CALG.getInstance(chosenConfidentialityAlgorithm); + outCipher = CALG.getInstance(chosenConfidentialityAlgorithm); + } + // instantiate hmacs if integrity protection filter is active + if (chosenIntegrityAlgorithm != null) + { + if (DEBUG && debuglevel > 2) + debug(INFO, "Activating integrity protection filter"); + inMac = IALG.getInstance(chosenIntegrityAlgorithm); + outMac = IALG.getInstance(chosenIntegrityAlgorithm); + } + } + else + { // same session new Keys + K = srp.generateKn(K, cn, sn); + } + + final KDF kdf = KDF.getInstance(K); + + // initialise in/out ciphers if confidentiality protection is used + if (inCipher != null) + { + inCipher.init(kdf, sIV, Direction.REVERSED); + outCipher.init(kdf, cIV, Direction.FORWARD); + } + // initialise in/out macs if integrity protection is used + if (inMac != null) + { + inMac.init(kdf); + outMac.init(kdf); + } + + if (sid != null && sid.length != 0) + { // update the security context and save in map + if (DEBUG && debuglevel > 2) + debug(INFO, "Updating security context for UID = " + uid); + ClientStore.instance().cacheSession( + uid, + ttl, + new SecurityContext( + srp.getAlgorithm(), + sid, + K, + cIV, + sIV, + replayDetection, + inCounter, + outCounter, + inMac, outMac, + inCipher, + outCipher)); + } + } + + private PRNG getDefaultPRNG() + { + if (prng == null) + prng = PRNG.getInstance(); + + return prng; + } +}
\ No newline at end of file diff --git a/libjava/classpath/gnu/javax/crypto/sasl/srp/SRPRegistry.java b/libjava/classpath/gnu/javax/crypto/sasl/srp/SRPRegistry.java new file mode 100644 index 00000000000..262cbcba305 --- /dev/null +++ b/libjava/classpath/gnu/javax/crypto/sasl/srp/SRPRegistry.java @@ -0,0 +1,219 @@ +/* SRPRegistry.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a 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 of the License, 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; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, 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.javax.crypto.sasl.srp; + +import gnu.java.security.Registry; + +/** + * <p>A list of key names designating the values exchanged between the server + * and client in an SRP communication authentication phase.</p> + */ +public interface SRPRegistry +{ + + /** Indices of (N, g) parameter values for SRP (.conf) password database. */ + String N_2048_BITS = "1"; + + String N_1536_BITS = "2"; + + String N_1280_BITS = "3"; + + String N_1024_BITS = "4"; + + String N_768_BITS = "5"; + + String N_640_BITS = "6"; + + String N_512_BITS = "7"; + + /** Available hash algorithms for all SRP calculations. */ + String[] SRP_ALGORITHMS = { Registry.SHA160_HASH, // the default one + Registry.MD5_HASH, Registry.RIPEMD128_HASH, + Registry.RIPEMD160_HASH, + + Registry.SHA256_HASH, Registry.SHA384_HASH, + Registry.SHA512_HASH }; + + /** + * The name of the default message digest algorithm to use when no name is + * explicitely given. In this implementation it is the <b>first</b> among + * those supported; i.e. the algorithm at index position #0: SHA with + * 160-bit output. + */ + String SRP_DEFAULT_DIGEST_NAME = SRP_ALGORITHMS[0]; + + /** + * The property name of the message digest algorithm name to use in a given + * SRP incarnation. + */ + String SRP_DIGEST_NAME = "srp.digest.name"; + + /** The public shared modulus: n. */ + String SHARED_MODULUS = "srp.N"; + + /** The GF generator used: g. */ + String FIELD_GENERATOR = "srp.g"; + + /** The list of server's available security options. */ + String AVAILABLE_OPTIONS = "srp.L"; + + /** The client's chosen security options. */ + String CHOSEN_OPTIONS = "srp.o"; + + /** The client's username. */ + String USER_NAME = "srp.U"; + + /** The client's authorization ID. */ + String USER_ROLE = "srp.I"; + + /** The user's salt. */ + String USER_SALT = "srp.s"; + + /** The user's password verifier. */ + String PASSWORD_VERIFIER = "srp.v"; + + /** The client's public ephemeral exponent: A. */ + String CLIENT_PUBLIC_KEY = "srp.A"; + + /** The server's public ephemeral exponent: B. */ + String SERVER_PUBLIC_KEY = "srp.B"; + + /** The client's evidence: M1. */ + String CLIENT_EVIDENCE = "srp.M1"; + + /** The server's evidence: M2. */ + String SERVER_EVIDENCE = "srp.M2"; + + /** Name of underlying hash algorithm for use with all SRP calculations. */ + String SRP_HASH = "gnu.crypto.sasl.srp.hash"; + + /** Name of SRP mandatory service property. */ + String SRP_MANDATORY = "gnu.crypto.sasl.srp.mandatory"; + + /** Name of SRP replay detection property. */ + String SRP_REPLAY_DETECTION = "gnu.crypto.sasl.srp.replay.detection"; + + /** Name of SRP integrity protection property. */ + String SRP_INTEGRITY_PROTECTION = "gnu.crypto.sasl.srp.integrity"; + + /** Name of SRP confidentiality protection property. */ + String SRP_CONFIDENTIALITY = "gnu.crypto.sasl.srp.confidentiality"; + + /** Name of the main SRP password file pathname property. */ + String PASSWORD_FILE = "gnu.crypto.sasl.srp.password.file"; + + /** + * Name of the SRP password database property --a reference to + * {@link gnu.crypto.sasl.srp.PasswordFile} object. + */ + String PASSWORD_DB = "gnu.crypto.sasl.srp.password.db"; + + /** Default fully qualified pathname of the SRP password file. */ + String DEFAULT_PASSWORD_FILE = "/etc/tpasswd"; + + /** Default value for replay detection security service. */ + boolean DEFAULT_REPLAY_DETECTION = true; + + /** Default value for integrity protection security service. */ + boolean DEFAULT_INTEGRITY = true; // implied by the previous option + + /** Default value for confidentiality protection security service. */ + boolean DEFAULT_CONFIDENTIALITY = false; + + // constants defining HMAC names + String HMAC_SHA1 = "hmac-sha1"; + + String HMAC_MD5 = "hmac-md5"; + + String HMAC_RIPEMD_160 = "hmac-ripemd-160"; + + /** Available HMAC algorithms for integrity protection. */ + String[] INTEGRITY_ALGORITHMS = { HMAC_SHA1, HMAC_MD5, HMAC_RIPEMD_160 }; + + // constants defining Cipher names + String AES = "aes"; + + String BLOWFISH = "blowfish"; + + /** Available Cipher algorithms for confidentiality protection. */ + String[] CONFIDENTIALITY_ALGORITHMS = { AES, BLOWFISH }; + + /** String for mandatory replay detection. */ + String OPTION_MANDATORY = "mandatory"; + + /** String for mda: the SRP digest algorithm name. */ + String OPTION_SRP_DIGEST = "mda"; + + /** String for mandatory replay detection. */ + String OPTION_REPLAY_DETECTION = "replay_detection"; + + /** String for mandatory integrity protection. */ + String OPTION_INTEGRITY = "integrity"; + + /** String for mandatory confidentiality protection. */ + String OPTION_CONFIDENTIALITY = "confidentiality"; + + /** String for mandatory replay detection. */ + String OPTION_MAX_BUFFER_SIZE = "maxbuffersize"; + + /** String for no mandatory security service. */ + String MANDATORY_NONE = "none"; + + /** Default mandatory security service required. */ + // String DEFAULT_MANDATORY = MANDATORY_NONE; + String DEFAULT_MANDATORY = OPTION_REPLAY_DETECTION; + + // String DEFAULT_MANDATORY = OPTION_INTEGRITY; + // String DEFAULT_MANDATORY = OPTION_CONFIDENTIALITY; + + /** Name of the UID field in the plain password file. */ + String MD_NAME_FIELD = "srp.md.name"; + + /** Name of the GID field in the plain password file. */ + String USER_VERIFIER_FIELD = "srp.user.verifier"; + + /** Name of the GECOS field in the plain password file. */ + String SALT_FIELD = "srp.salt"; + + /** Name of the SHELL field in the plain password file. */ + String CONFIG_NDX_FIELD = "srp.config.ndx"; + + /** Minimum bitlength of the SRP public modulus. */ + int MINIMUM_MODULUS_BITLENGTH = 512; +}
\ No newline at end of file diff --git a/libjava/classpath/gnu/javax/crypto/sasl/srp/SRPServer.java b/libjava/classpath/gnu/javax/crypto/sasl/srp/SRPServer.java new file mode 100644 index 00000000000..11902b82ed8 --- /dev/null +++ b/libjava/classpath/gnu/javax/crypto/sasl/srp/SRPServer.java @@ -0,0 +1,1156 @@ +/* SRPServer.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a 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 of the License, 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; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, 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.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; +import gnu.javax.crypto.cipher.CipherFactory; +import gnu.javax.crypto.cipher.IBlockCipher; +import gnu.javax.crypto.key.IKeyAgreementParty; +import gnu.javax.crypto.key.KeyAgreementFactory; +import gnu.javax.crypto.key.KeyAgreementException; +import gnu.javax.crypto.key.OutgoingMessage; +import gnu.javax.crypto.key.IncomingMessage; +import gnu.javax.crypto.key.srp6.SRP6KeyAgreement; +import gnu.javax.crypto.sasl.IllegalMechanismStateException; +import gnu.javax.crypto.sasl.InputBuffer; +import gnu.javax.crypto.sasl.IntegrityException; +import gnu.javax.crypto.sasl.OutputBuffer; +import gnu.javax.crypto.sasl.ServerMechanism; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.ByteArrayOutputStream; +import java.io.UnsupportedEncodingException; +import java.math.BigInteger; +import java.util.Arrays; +import java.util.HashMap; +import java.util.StringTokenizer; + +import javax.security.sasl.AuthenticationException; +import javax.security.sasl.SaslException; +import javax.security.sasl.SaslServer; + +/** + * <p>The SASL-SRP server-side mechanism.</p> + * + * @version $Revision: 1.2 $ + */ +public class SRPServer extends ServerMechanism implements SaslServer +{ + + // Debugging methods and variables + // ------------------------------------------------------------------------- + + private static final String NAME = "SRPServer"; + + // private static final String ERROR = "ERROR"; + private static final String WARN = " WARN"; + + private static final String INFO = " INFO"; + + private static final String TRACE = "DEBUG"; + + private static final boolean DEBUG = true; + + private static final int debuglevel = 3; + + private static final PrintWriter err = new PrintWriter(System.out, true); + + private static void debug(final String level, final Object obj) + { + err.println("[" + level + "] " + NAME + ": " + String.valueOf(obj)); + } + + // Constants and variables + // ------------------------------------------------------------------------- + + private String U = null; // client's username + + private BigInteger N, g, A, B; + + private byte[] s; // salt + + private byte[] cIV, sIV; // client+server IVs, when confidentiality is on + + private byte[] cn, sn; // client's and server's nonce + + private SRP srp; // SRP algorithm instance used by this server + + private byte[] sid; // session ID when re-used + + private int ttl = 360; // session time-to-live in seconds + + private byte[] cCB; // peer's channel binding' + + private String mandatory; // List of available options + + private String L = null; + + private String o; + + private String chosenIntegrityAlgorithm; + + private String chosenConfidentialityAlgorithm; + + private int rawSendSize = Registry.SASL_BUFFER_MAX_LIMIT; + + private byte[] K; // shared session key + + private boolean replayDetection = true; // whether Replay Detection is on + + private int inCounter = 0; // messages sequence numbers + + private int outCounter = 0; + + private IALG inMac, outMac; // if !null, use for integrity + + private CALG inCipher, outCipher; // if !null, use for confidentiality + + private IKeyAgreementParty serverHandler = KeyAgreementFactory.getPartyBInstance(Registry.SRP_SASL_KA); + + /** Our default source of randomness. */ + private PRNG prng = null; + + // Constructor(s) + // ------------------------------------------------------------------------- + + public SRPServer() + { + super(Registry.SASL_SRP_MECHANISM); + } + + // Class methods + // ------------------------------------------------------------------------- + + // Instance methods + // ------------------------------------------------------------------------- + + // abstract methods implementation ----------------------------------------- + + protected void initMechanism() throws SaslException + { + // TODO: + // we must have a means to map a given username to a preferred + // SRP hash algorithm; otherwise we end up using _always_ SHA. + // for the time being get it from the mechanism properties map + // and apply it for all users. + final String mda = (String) properties.get(SRPRegistry.SRP_HASH); + srp = SRP.instance(mda == null ? SRPRegistry.SRP_DEFAULT_DIGEST_NAME : mda); + } + + protected void resetMechanism() throws SaslException + { + s = null; + A = B = null; + K = null; + inMac = outMac = null; + inCipher = outCipher = null; + + sid = null; + } + + // javax.security.sasl.SaslServer interface implementation ----------------- + + public byte[] evaluateResponse(final byte[] response) throws SaslException + { + switch (state) + { + case 0: + if (response == null) + { + return null; + } + state++; + return sendProtocolElements(response); + case 1: + if (!complete) + { + state++; + return sendEvidence(response); + } + // else fall through + default: + throw new IllegalMechanismStateException("evaluateResponse()"); + } + } + + protected byte[] engineUnwrap(final byte[] incoming, final int offset, + final int len) throws SaslException + { + // if (DEBUG && debuglevel > 8) debug(TRACE, "==> engineUnwrap()"); + // + // if (inMac == null && inCipher == null) { + // throw new IllegalStateException("connection is not protected"); + // } + // + // if (DEBUG && debuglevel > 6) debug(TRACE, "Incoming buffer (before security): "+Util.dumpString(incoming, offset, len)); + // + // byte[] data = null; + // try { + // InputBuffer frameIn = InputBuffer.getInstance(incoming, offset, len); + // data = frameIn.getEOS(); + // if (inMac != null) { + // byte[] received_mac = frameIn.getOS(); + // if (DEBUG && debuglevel > 6) debug(TRACE, "Got C (received MAC): "+Util.dumpString(received_mac)); + // inMac.update(data); + // if (replayDetection) { + // inCounter++; + // if (DEBUG && debuglevel > 6) debug(TRACE, "inCounter="+String.valueOf(inCounter)); + // inMac.update(new byte[] { + // (byte)(inCounter >>> 24), + // (byte)(inCounter >>> 16), + // (byte)(inCounter >>> 8), + // (byte) inCounter }); + // } + // final byte[] computed_mac = inMac.doFinal(); + // if (DEBUG && debuglevel > 6) debug(TRACE, "Computed MAC: "+Util.dumpString(computed_mac)); + // if (!Arrays.equals(received_mac, computed_mac)) + // throw new IntegrityException("engineUnwrap()"); + // } + // if (inCipher != null) { + // data = inCipher.doFinal(data); + // } + // } catch (IOException x) { + // if (x instanceof SaslException) { + // throw (SaslException) x; + // } + // throw new SaslException("engineUnwrap()", x); + // } + // + // if (DEBUG && debuglevel > 6) debug(TRACE, "Incoming buffer (after security): "+Util.dumpString(data)); + // if (DEBUG && debuglevel > 8) debug(TRACE, "<== engineUnwrap()"); + // return data; + + if (DEBUG && debuglevel > 8) + debug(TRACE, "==> engineUnwrap()"); + + if (inMac == null && inCipher == null) + { + throw new IllegalStateException("connection is not protected"); + } + + if (DEBUG && debuglevel > 6) + debug(TRACE, "Incoming buffer (before security): " + + Util.dumpString(incoming, offset, len)); + + // at this point one, or both, of confidentiality and integrity protection + // services are active. + + final byte[] result; + try + { + if (inMac != null) + { // integrity bytes are at the end of the stream + final int macBytesCount = inMac.length(); + final int payloadLength = len - macBytesCount; + final byte[] received_mac = new byte[macBytesCount]; + System.arraycopy(incoming, offset + payloadLength, received_mac, 0, + macBytesCount); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Got C (received MAC): " + + Util.dumpString(received_mac)); + inMac.update(incoming, offset, payloadLength); + if (replayDetection) + { + inCounter++; + if (DEBUG && debuglevel > 6) + debug(TRACE, "inCounter=" + String.valueOf(inCounter)); + inMac.update(new byte[] { (byte) (inCounter >>> 24), + (byte) (inCounter >>> 16), + (byte) (inCounter >>> 8), + (byte) inCounter }); + } + + final byte[] computed_mac = inMac.doFinal(); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Computed MAC: " + Util.dumpString(computed_mac)); + if (!Arrays.equals(received_mac, computed_mac)) + { + throw new IntegrityException("engineUnwrap()"); + } + + // deal with the payload, which can be either plain or encrypted + if (inCipher != null) + { + result = inCipher.doFinal(incoming, offset, payloadLength); + } + else + { + result = new byte[payloadLength]; + System.arraycopy(incoming, offset, result, 0, result.length); + } + } + else + { // no integrity protection; just confidentiality + // if (inCipher != null) { + result = inCipher.doFinal(incoming, offset, len); + // } else { + // result = new byte[len]; + // System.arraycopy(incoming, offset, result, 0, len); + // } + } + } + catch (IOException x) + { + if (x instanceof SaslException) + { + throw (SaslException) x; + } + throw new SaslException("engineUnwrap()", x); + } + if (DEBUG && debuglevel > 6) + debug(TRACE, "Incoming buffer (after security): " + + Util.dumpString(result)); + if (DEBUG && debuglevel > 8) + debug(TRACE, "<== engineUnwrap()"); + return result; + } + + protected byte[] engineWrap(final byte[] outgoing, final int offset, + final int len) throws SaslException + { + // if (DEBUG && debuglevel > 8) debug(TRACE, "==> engineWrap()"); + // + // if (outMac == null && outCipher == null) { + // throw new IllegalStateException("connection is not protected"); + // } + // + // byte[] data = new byte[len]; + // System.arraycopy(outgoing, offset, data, 0, len); + // + // if (DEBUG && debuglevel > 6) debug(TRACE, "Outgoing buffer (before security) (hex): "+Util.dumpString(data)); + // if (DEBUG && debuglevel > 6) debug(TRACE, "Outgoing buffer (before security) (str): \""+new String(data)+"\""); + // + // final byte[] result; + // try { + // OutputBuffer frameOut = new OutputBuffer(); + // // Process the data + // if (outCipher != null) { + // data = outCipher.doFinal(data); + // if (DEBUG && debuglevel > 6) debug(TRACE, "Encoding c (encrypted plaintext): "+Util.dumpString(data)); + // } else { + // if (DEBUG && debuglevel > 6) debug(TRACE, "Encoding p (plaintext): "+Util.dumpString(data)); + // } + // frameOut.setEOS(data); + // if (outMac != null) { + // outMac.update(data); + // if (replayDetection) { + // outCounter++; + // if (DEBUG && debuglevel > 6) debug(TRACE, "outCounter="+String.valueOf(outCounter)); + // outMac.update(new byte[] { + // (byte)(outCounter >>> 24), + // (byte)(outCounter >>> 16), + // (byte)(outCounter >>> 8), + // (byte) outCounter}); + // } + // byte[] C = outMac.doFinal(); + // frameOut.setOS(C); + // if (DEBUG && debuglevel > 6) debug(TRACE, "Encoding C (integrity checksum): "+Util.dumpString(C)); + // } + // result = frameOut.wrap(); + // + // } catch (IOException x) { + // if (x instanceof SaslException) { + // throw (SaslException) x; + // } + // throw new SaslException("engineWrap()", x); + // } + // + // if (DEBUG && debuglevel > 8) debug(TRACE, "<== engineWrap()"); + // return result; + + if (DEBUG && debuglevel > 8) + debug(TRACE, "==> engineWrap()"); + + if (outMac == null && outCipher == null) + { + throw new IllegalStateException("connection is not protected"); + } + + if (DEBUG && debuglevel > 6) + debug(TRACE, "Outgoing buffer (before security) (hex): " + + Util.dumpString(outgoing, offset, len)); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Outgoing buffer (before security) (str): \"" + + new String(outgoing, offset, len) + "\""); + + // at this point one, or both, of confidentiality and integrity protection + // services are active. + + byte[] result; + try + { + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + if (outCipher != null) + { + result = outCipher.doFinal(outgoing, offset, len); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Encoding c (encrypted plaintext): " + + Util.dumpString(result)); + + out.write(result); + + if (outMac != null) + { + outMac.update(result); + if (replayDetection) + { + outCounter++; + if (DEBUG && debuglevel > 6) + debug(TRACE, "outCounter=" + String.valueOf(outCounter)); + outMac.update(new byte[] { (byte) (outCounter >>> 24), + (byte) (outCounter >>> 16), + (byte) (outCounter >>> 8), + (byte) outCounter }); + } + final byte[] C = outMac.doFinal(); + out.write(C); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Encoding C (integrity checksum): " + + Util.dumpString(C)); + } // else ciphertext only; do nothing + } + else + { // no confidentiality; just integrity [+ replay detection] + if (DEBUG && debuglevel > 6) + debug(TRACE, "Encoding p (plaintext): " + + Util.dumpString(outgoing, offset, len)); + + out.write(outgoing, offset, len); + + // if (outMac != null) { + outMac.update(outgoing, offset, len); + if (replayDetection) + { + outCounter++; + if (DEBUG && debuglevel > 6) + debug(TRACE, "outCounter=" + String.valueOf(outCounter)); + outMac.update(new byte[] { (byte) (outCounter >>> 24), + (byte) (outCounter >>> 16), + (byte) (outCounter >>> 8), + (byte) outCounter }); + } + final byte[] C = outMac.doFinal(); + out.write(C); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Encoding C (integrity checksum): " + + Util.dumpString(C)); + // } // else plaintext only; do nothing + } + + result = out.toByteArray(); + + } + catch (IOException x) + { + if (x instanceof SaslException) + { + throw (SaslException) x; + } + throw new SaslException("engineWrap()", x); + } + + if (DEBUG && debuglevel > 8) + debug(TRACE, "<== engineWrap()"); + return result; + } + + protected String getNegotiatedQOP() + { + if (inMac != null) + { + if (inCipher != null) + { + return Registry.QOP_AUTH_CONF; + } + else + { + return Registry.QOP_AUTH_INT; + } + } + return Registry.QOP_AUTH; + } + + protected String getNegotiatedStrength() + { + if (inMac != null) + { + if (inCipher != null) + { + return Registry.STRENGTH_HIGH; + } + else + { + return Registry.STRENGTH_MEDIUM; + } + } + return Registry.STRENGTH_LOW; + } + + protected String getNegotiatedRawSendSize() + { + return String.valueOf(rawSendSize); + } + + protected String getReuse() + { + return Registry.REUSE_TRUE; + } + + // other methods ----------------------------------------------------------- + + private byte[] sendProtocolElements(final byte[] input) throws SaslException + { + if (DEBUG && debuglevel > 8) + debug(TRACE, "==> sendProtocolElements()"); + if (DEBUG && debuglevel > 6) + debug(TRACE, "C: " + Util.dumpString(input)); + + // Client send U, I, sid, cn + final InputBuffer frameIn = new InputBuffer(input); + try + { + U = frameIn.getText(); // Extract username + if (DEBUG && debuglevel > 6) + debug(TRACE, "Got U (username): \"" + U + "\""); + authorizationID = frameIn.getText(); // Extract authorisation ID + if (DEBUG && debuglevel > 6) + debug(TRACE, "Got I (userid): \"" + authorizationID + "\""); + sid = frameIn.getEOS(); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Got sid (session ID): " + new String(sid)); + cn = frameIn.getOS(); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Got cn (client nonce): " + Util.dumpString(cn)); + cCB = frameIn.getEOS(); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Got cCB (client channel binding): " + + Util.dumpString(cCB)); + } + catch (IOException x) + { + if (x instanceof SaslException) + { + throw (SaslException) x; + } + throw new AuthenticationException("sendProtocolElements()", x); + } + + // do/can we re-use? + if (ServerStore.instance().isAlive(sid)) + { + final SecurityContext ctx = ServerStore.instance().restoreSession(sid); + srp = SRP.instance(ctx.getMdName()); + K = ctx.getK(); + cIV = ctx.getClientIV(); + sIV = ctx.getServerIV(); + replayDetection = ctx.hasReplayDetection(); + inCounter = ctx.getInCounter(); + outCounter = ctx.getOutCounter(); + inMac = ctx.getInMac(); + outMac = ctx.getOutMac(); + inCipher = ctx.getInCipher(); + outCipher = ctx.getOutCipher(); + + if (sn == null || sn.length != 16) + { + sn = new byte[16]; + } + getDefaultPRNG().nextBytes(sn); + + setupSecurityServices(false); + + final OutputBuffer frameOut = new OutputBuffer(); + try + { + frameOut.setScalar(1, 0xFF); + frameOut.setOS(sn); + frameOut.setEOS(channelBinding); + } + catch (IOException x) + { + if (x instanceof SaslException) + { + throw (SaslException) x; + } + throw new AuthenticationException("sendProtocolElements()", x); + } + final byte[] result = frameOut.encode(); + if (DEBUG && debuglevel > 8) + debug(TRACE, "<== sendProtocolElements()"); + if (DEBUG && debuglevel > 2) + debug(INFO, "Old session..."); + if (DEBUG && debuglevel > 2) + debug(INFO, "S: " + Util.dumpString(result)); + if (DEBUG && debuglevel > 2) + debug(INFO, " sn = " + Util.dumpString(sn)); + if (DEBUG && debuglevel > 2) + debug(INFO, " sCB = " + Util.dumpString(channelBinding)); + return result; + } + else + { // new session + authenticator.activate(properties); + + // ------------------------------------------------------------------- + final HashMap mapB = new HashMap(); + // mapB.put(SRP6KeyAgreement.HASH_FUNCTION, srp.newDigest()); + mapB.put(SRP6KeyAgreement.HASH_FUNCTION, srp.getAlgorithm()); + mapB.put(SRP6KeyAgreement.HOST_PASSWORD_DB, authenticator); + + try + { + serverHandler.init(mapB); + OutgoingMessage out = new OutgoingMessage(); + out.writeString(U); + IncomingMessage in = new IncomingMessage(out.toByteArray()); + out = serverHandler.processMessage(in); + + in = new IncomingMessage(out.toByteArray()); + N = in.readMPI(); + g = in.readMPI(); + s = in.readMPI().toByteArray(); + B = in.readMPI(); + } + catch (KeyAgreementException x) + { + throw new SaslException("sendProtocolElements()", x); + } + // ------------------------------------------------------------------- + + if (DEBUG && debuglevel > 6) + debug(TRACE, "Encoding N (modulus): " + Util.dump(N)); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Encoding g (generator): " + Util.dump(g)); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Encoding s (client's salt): " + Util.dumpString(s)); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Encoding B (server ephemeral public key): " + + Util.dump(B)); + + // The server creates an options list (L), which consists of a + // comma-separated list of option strings that specify the security + // service options the server supports. + L = createL(); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Encoding L (available options): \"" + L + "\""); + if (DEBUG && debuglevel > 6) + debug(TRACE, "Encoding sIV (server IV): " + Util.dumpString(sIV)); + + final OutputBuffer frameOut = new OutputBuffer(); + try + { + frameOut.setScalar(1, 0x00); + frameOut.setMPI(N); + frameOut.setMPI(g); + frameOut.setOS(s); + frameOut.setMPI(B); + frameOut.setText(L); + } + catch (IOException x) + { + if (x instanceof SaslException) + { + throw (SaslException) x; + } + throw new AuthenticationException("sendProtocolElements()", x); + } + final byte[] result = frameOut.encode(); + if (DEBUG && debuglevel > 8) + debug(TRACE, "<== sendProtocolElements()"); + if (DEBUG && debuglevel > 2) + debug(INFO, "New session..."); + if (DEBUG && debuglevel > 2) + debug(INFO, "S: " + Util.dumpString(result)); + if (DEBUG && debuglevel > 2) + debug(INFO, " N = 0x" + N.toString(16)); + if (DEBUG && debuglevel > 2) + debug(INFO, " g = 0x" + g.toString(16)); + if (DEBUG && debuglevel > 2) + debug(INFO, " s = " + Util.dumpString(s)); + if (DEBUG && debuglevel > 2) + debug(INFO, " B = 0x" + B.toString(16)); + if (DEBUG && debuglevel > 2) + debug(INFO, " L = " + L); + return result; + } + } + + private byte[] sendEvidence(final byte[] input) throws SaslException + { + if (DEBUG && debuglevel > 8) + debug(TRACE, "==> sendEvidence()"); + if (DEBUG && debuglevel > 6) + debug(TRACE, "C: " + Util.dumpString(input)); + + // Client send A, M1, o, cIV + final InputBuffer frameIn = new InputBuffer(input); + final byte[] M1; + try + { + A = frameIn.getMPI(); // Extract client's ephemeral public key + if (DEBUG && debuglevel > 6) + debug(TRACE, "Got A (client ephemeral public key): " + Util.dump(A)); + M1 = frameIn.getOS(); // Extract evidence + if (DEBUG && debuglevel > 6) + debug(TRACE, "Got M1 (client evidence): " + Util.dumpString(M1)); + o = frameIn.getText(); // Extract client's options list + if (DEBUG && debuglevel > 6) + debug(TRACE, "Got o (client chosen options): \"" + o + "\""); + cIV = frameIn.getOS(); // Extract client's IV + if (DEBUG && debuglevel > 6) + debug(TRACE, "Got cIV (client IV): " + Util.dumpString(cIV)); + } + catch (IOException x) + { + if (x instanceof SaslException) + { + throw (SaslException) x; + } + throw new AuthenticationException("sendEvidence()", x); + } + + // Parse client's options and set security layer variables + parseO(o); + + // ---------------------------------------------------------------------- + try + { + final OutgoingMessage out = new OutgoingMessage(); + out.writeMPI(A); + final IncomingMessage in = new IncomingMessage(out.toByteArray()); + serverHandler.processMessage(in); + K = serverHandler.getSharedSecret(); + } + catch (KeyAgreementException x) + { + throw new SaslException("sendEvidence()", x); + } + // ---------------------------------------------------------------------- + + if (DEBUG && debuglevel > 6) + debug(TRACE, "K: " + Util.dumpString(K)); + + final byte[] expected; + try + { + expected = srp.generateM1(N, g, U, s, A, B, K, authorizationID, L, cn, + cCB); + } + catch (UnsupportedEncodingException x) + { + throw new AuthenticationException("sendEvidence()", x); + } + + // Verify client evidence + if (!Arrays.equals(M1, expected)) + { + throw new AuthenticationException("M1 mismatch"); + } + + setupSecurityServices(true); + + final byte[] M2; + try + { + M2 = srp.generateM2(A, M1, K, U, authorizationID, o, sid, ttl, cIV, + sIV, channelBinding); + } + catch (UnsupportedEncodingException x) + { + throw new AuthenticationException("sendEvidence()", x); + } + + final OutputBuffer frameOut = new OutputBuffer(); + try + { + frameOut.setOS(M2); + frameOut.setOS(sIV); + frameOut.setEOS(sid); + frameOut.setScalar(4, ttl); + frameOut.setEOS(channelBinding); + } + catch (IOException x) + { + if (x instanceof SaslException) + { + throw (SaslException) x; + } + throw new AuthenticationException("sendEvidence()", x); + } + final byte[] result = frameOut.encode(); + if (DEBUG && debuglevel > 2) + debug(INFO, "S: " + Util.dumpString(result)); + if (DEBUG && debuglevel > 2) + debug(INFO, " M2 = " + Util.dumpString(M2)); + if (DEBUG && debuglevel > 2) + debug(INFO, " sIV = " + Util.dumpString(sIV)); + if (DEBUG && debuglevel > 2) + debug(INFO, " sid = " + new String(sid)); + if (DEBUG && debuglevel > 2) + debug(INFO, " ttl = " + ttl); + if (DEBUG && debuglevel > 2) + debug(INFO, " sCB = " + Util.dumpString(channelBinding)); + + if (DEBUG && debuglevel > 8) + debug(TRACE, "<== sendEvidence()"); + return result; + } + + private String createL() + { + if (DEBUG && debuglevel > 8) + debug(TRACE, "==> createL()"); + + String s = (String) properties.get(SRPRegistry.SRP_MANDATORY); + if (s == null) + { + s = SRPRegistry.DEFAULT_MANDATORY; + } + if (!SRPRegistry.MANDATORY_NONE.equals(s) + && !SRPRegistry.OPTION_REPLAY_DETECTION.equals(s) + && !SRPRegistry.OPTION_INTEGRITY.equals(s) + && !SRPRegistry.OPTION_CONFIDENTIALITY.equals(s)) + { + if (DEBUG && debuglevel > 4) + debug(WARN, "Unrecognised mandatory option (" + s + + "). Using default..."); + s = SRPRegistry.DEFAULT_MANDATORY; + } + + mandatory = s; + + s = (String) properties.get(SRPRegistry.SRP_CONFIDENTIALITY); + final boolean confidentiality = (s == null ? SRPRegistry.DEFAULT_CONFIDENTIALITY + : Boolean.valueOf(s).booleanValue()); + + s = (String) properties.get(SRPRegistry.SRP_INTEGRITY_PROTECTION); + boolean integrity = (s == null ? SRPRegistry.DEFAULT_INTEGRITY + : Boolean.valueOf(s).booleanValue()); + + s = (String) properties.get(SRPRegistry.SRP_REPLAY_DETECTION); + final boolean replayDetection = (s == null ? SRPRegistry.DEFAULT_REPLAY_DETECTION + : Boolean.valueOf(s).booleanValue()); + + final StringBuffer sb = new StringBuffer(); + sb.append(SRPRegistry.OPTION_SRP_DIGEST).append("=").append( + srp.getAlgorithm()).append( + ","); + + if (!SRPRegistry.MANDATORY_NONE.equals(mandatory)) + { + sb.append(SRPRegistry.OPTION_MANDATORY).append("=").append(mandatory).append( + ","); + } + if (replayDetection) + { + sb.append(SRPRegistry.OPTION_REPLAY_DETECTION).append(","); + // if replay detection is on then force integrity protection + integrity = true; + } + + int i; + if (integrity) + { + for (i = 0; i < SRPRegistry.INTEGRITY_ALGORITHMS.length; i++) + { + sb.append(SRPRegistry.OPTION_INTEGRITY).append("=").append( + SRPRegistry.INTEGRITY_ALGORITHMS[i]).append( + ","); + } + } + + if (confidentiality) + { + IBlockCipher cipher; + for (i = 0; i < SRPRegistry.CONFIDENTIALITY_ALGORITHMS.length; i++) + { + cipher = CipherFactory.getInstance(SRPRegistry.CONFIDENTIALITY_ALGORITHMS[i]); + if (cipher != null) + { + sb.append(SRPRegistry.OPTION_CONFIDENTIALITY).append("=").append( + SRPRegistry.CONFIDENTIALITY_ALGORITHMS[i]).append( + ","); + } + } + } + + final String result = sb.append(SRPRegistry.OPTION_MAX_BUFFER_SIZE).append( + "=").append( + Registry.SASL_BUFFER_MAX_LIMIT).toString(); + if (DEBUG && debuglevel > 8) + debug(TRACE, "<== createL()"); + return result; + } + + // Parse client's options and set security layer variables + private void parseO(final String o) throws AuthenticationException + { + this.replayDetection = false; + boolean integrity = false; + boolean confidentiality = false; + String option; + int i; + + final StringTokenizer st = new StringTokenizer(o.toLowerCase(), ","); + while (st.hasMoreTokens()) + { + option = st.nextToken(); + if (DEBUG && debuglevel > 6) + debug(TRACE, "option: <" + option + ">"); + if (option.equals(SRPRegistry.OPTION_REPLAY_DETECTION)) + { + replayDetection = true; + } + else if (option.startsWith(SRPRegistry.OPTION_INTEGRITY + "=")) + { + if (integrity) + { + throw new AuthenticationException( + "Only one integrity algorithm may be chosen"); + } + else + { + option = option.substring(option.indexOf('=') + 1); + if (DEBUG && debuglevel > 6) + debug(TRACE, "algorithm: <" + option + ">"); + for (i = 0; i < SRPRegistry.INTEGRITY_ALGORITHMS.length; i++) + { + if (SRPRegistry.INTEGRITY_ALGORITHMS[i].equals(option)) + { + chosenIntegrityAlgorithm = option; + integrity = true; + break; + } + } + if (!integrity) + { + throw new AuthenticationException( + "Unknown integrity algorithm: " + + option); + } + } + } + else if (option.startsWith(SRPRegistry.OPTION_CONFIDENTIALITY + "=")) + { + if (confidentiality) + { + throw new AuthenticationException( + "Only one confidentiality algorithm may be chosen"); + } + else + { + option = option.substring(option.indexOf('=') + 1); + if (DEBUG && debuglevel > 6) + debug(TRACE, "algorithm: <" + option + ">"); + for (i = 0; i < SRPRegistry.CONFIDENTIALITY_ALGORITHMS.length; i++) + { + if (SRPRegistry.CONFIDENTIALITY_ALGORITHMS[i].equals(option)) + { + chosenConfidentialityAlgorithm = option; + confidentiality = true; + break; + } + } + if (!confidentiality) + { + throw new AuthenticationException( + "Unknown confidentiality algorithm: " + + option); + } + } + } + else if (option.startsWith(SRPRegistry.OPTION_MAX_BUFFER_SIZE + "=")) + { + final String maxBufferSize = option.substring(option.indexOf('=') + 1); + try + { + rawSendSize = Integer.parseInt(maxBufferSize); + if (rawSendSize > Registry.SASL_BUFFER_MAX_LIMIT + || rawSendSize < 1) + throw new AuthenticationException( + "Illegal value for 'maxbuffersize' option"); + } + catch (NumberFormatException x) + { + throw new AuthenticationException( + SRPRegistry.OPTION_MAX_BUFFER_SIZE + + "=" + + String.valueOf(maxBufferSize), + x); + } + } + } + + // check if client did the right thing + if (replayDetection) + { + if (!integrity) + { + throw new AuthenticationException( + "Missing integrity protection algorithm " + + "but replay detection is chosen"); + } + } + if (mandatory.equals(SRPRegistry.OPTION_REPLAY_DETECTION)) + { + if (!replayDetection) + { + throw new AuthenticationException( + "Replay detection is mandatory but was not chosen"); + } + } + if (mandatory.equals(SRPRegistry.OPTION_INTEGRITY)) + { + if (!integrity) + { + throw new AuthenticationException( + "Integrity protection is mandatory but was not chosen"); + } + } + if (mandatory.equals(SRPRegistry.OPTION_CONFIDENTIALITY)) + { + if (!confidentiality) + { + throw new AuthenticationException( + "Confidentiality is mandatory but was not chosen"); + } + } + + int blockSize = 0; + if (chosenConfidentialityAlgorithm != null) + { + final IBlockCipher cipher = CipherFactory.getInstance(chosenConfidentialityAlgorithm); + if (cipher != null) + { + blockSize = cipher.defaultBlockSize(); + } + else + { // should not happen + throw new AuthenticationException("Confidentiality algorithm (" + + chosenConfidentialityAlgorithm + + ") not available"); + } + } + + sIV = new byte[blockSize]; + if (blockSize > 0) + getDefaultPRNG().nextBytes(sIV); + } + + private void setupSecurityServices(final boolean newSession) + throws SaslException + { + complete = true; // signal end of authentication phase + if (newSession) + { + outCounter = inCounter = 0; + // instantiate cipher if confidentiality protection filter is active + if (chosenConfidentialityAlgorithm != null) + { + if (DEBUG && debuglevel > 2) + debug(INFO, "Activating confidentiality protection filter"); + inCipher = CALG.getInstance(chosenConfidentialityAlgorithm); + outCipher = CALG.getInstance(chosenConfidentialityAlgorithm); + } + // instantiate hmacs if integrity protection filter is active + if (chosenIntegrityAlgorithm != null) + { + if (DEBUG && debuglevel > 2) + debug(INFO, "Activating integrity protection filter"); + inMac = IALG.getInstance(chosenIntegrityAlgorithm); + outMac = IALG.getInstance(chosenIntegrityAlgorithm); + } + + // generate a new sid if at least integrity is used + sid = (inMac != null ? ServerStore.getNewSessionID() : new byte[0]); + } + else + { // same session new keys + K = srp.generateKn(K, cn, sn); + } + + final KDF kdf = KDF.getInstance(K); + + // initialise in/out ciphers if confidentaility protection is used + if (inCipher != null) + { + outCipher.init(kdf, sIV, Direction.FORWARD); + inCipher.init(kdf, cIV, Direction.REVERSED); + } + // initialise in/out macs if integrity protection is used + if (inMac != null) + { + outMac.init(kdf); + inMac.init(kdf); + } + + if (sid != null && sid.length != 0) + { // update the security context and save in map + if (DEBUG && debuglevel > 2) + debug(INFO, "Updating security context for sid = " + new String(sid)); + ServerStore.instance().cacheSession( + ttl, + new SecurityContext( + srp.getAlgorithm(), + sid, + K, + cIV, + sIV, + replayDetection, + inCounter, + outCounter, + inMac, outMac, + inCipher, + outCipher)); + } + } + + private PRNG getDefaultPRNG() + { + if (prng == null) + prng = PRNG.getInstance(); + + return prng; + } +}
\ No newline at end of file diff --git a/libjava/classpath/gnu/javax/crypto/sasl/srp/SecurityContext.java b/libjava/classpath/gnu/javax/crypto/sasl/srp/SecurityContext.java new file mode 100644 index 00000000000..feca25cadb8 --- /dev/null +++ b/libjava/classpath/gnu/javax/crypto/sasl/srp/SecurityContext.java @@ -0,0 +1,164 @@ +/* SecurityContext.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a 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 of the License, 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; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, 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.javax.crypto.sasl.srp; + +/** + * <p>A package-private placeholder for an SRP security context.</p> + */ +class SecurityContext +{ + + // Constants and variables + // ------------------------------------------------------------------------- + + private String mdName; + + private byte[] sid; + + private byte[] K; + + private byte[] cIV; + + private byte[] sIV; + + private boolean replayDetection; + + private int inCounter; + + private int outCounter; + + private IALG inMac; + + private IALG outMac; + + private CALG inCipher; + + private CALG outCipher; + + // Constructor(s) + // ------------------------------------------------------------------------- + + SecurityContext(final String mdName, final byte[] sid, final byte[] K, + final byte[] cIV, final byte[] sIV, + final boolean replayDetection, final int inCounter, + final int outCounter, final IALG inMac, final IALG outMac, + final CALG inCipher, final CALG outCipher) + { + super(); + + this.mdName = mdName; + this.sid = sid; + this.K = K; + this.cIV = cIV; + this.sIV = sIV; + this.replayDetection = replayDetection; + this.inCounter = inCounter; + this.outCounter = outCounter; + this.inMac = inMac; + this.outMac = outMac; + this.inCipher = inCipher; + this.outCipher = outCipher; + } + + // Class methods + // ------------------------------------------------------------------------- + + // Instance methods + // ------------------------------------------------------------------------- + + String getMdName() + { + return mdName; + } + + byte[] getSID() + { + return sid; + } + + byte[] getK() + { + return K; + } + + byte[] getClientIV() + { + return cIV; + } + + byte[] getServerIV() + { + return sIV; + } + + boolean hasReplayDetection() + { + return replayDetection; + } + + int getInCounter() + { + return inCounter; + } + + int getOutCounter() + { + return outCounter; + } + + IALG getInMac() + { + return inMac; + } + + IALG getOutMac() + { + return outMac; + } + + CALG getInCipher() + { + return inCipher; + } + + CALG getOutCipher() + { + return outCipher; + } +}
\ No newline at end of file diff --git a/libjava/classpath/gnu/javax/crypto/sasl/srp/ServerStore.java b/libjava/classpath/gnu/javax/crypto/sasl/srp/ServerStore.java new file mode 100644 index 00000000000..99bf96a9444 --- /dev/null +++ b/libjava/classpath/gnu/javax/crypto/sasl/srp/ServerStore.java @@ -0,0 +1,196 @@ +/* ServerStore.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a 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 of the License, 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; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, 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.javax.crypto.sasl.srp; + +import java.util.HashMap; + +/** + * <p>The server-side implementation of the SRP security context store.</p> + */ +public class ServerStore +{ + + // Constants and variables + // ------------------------------------------------------------------------- + + /** The underlying singleton. */ + private static ServerStore singleton = null; + + /** The map of sid --> Security Context record. */ + private static final HashMap sid2ssc = new HashMap(); + + /** The map of sid --> Session timing record. */ + private static final HashMap sid2ttl = new HashMap(); + + /** A synchronisation lock. */ + private static final Object lock = new Object(); + + /** A counter to generate legible SIDs. */ + private static int counter = 0; + + // Constructor(s) + // ------------------------------------------------------------------------- + + /** Private constructor to enforce Singleton pattern. */ + private ServerStore() + { + super(); + + // TODO: add a cleaning timer thread + } + + // Class methods + // ------------------------------------------------------------------------- + + /** + * <p>Returns the classloader Singleton.</p> + * + * @return the classloader Singleton instance. + */ + static synchronized final ServerStore instance() + { + if (singleton == null) + { + singleton = new ServerStore(); + } + return singleton; + } + + /** + * <p>Returns a legible new session identifier.</p> + * + * @return a new session identifier. + */ + static synchronized final byte[] getNewSessionID() + { + final String sid = String.valueOf(++counter); + return new StringBuffer("SID-").append( + "0000000000".substring( + 0, + 10 - sid.length())).append( + sid).toString().getBytes(); + } + + // Instance methods + // ------------------------------------------------------------------------- + + /** + * <p>Returns a boolean flag indicating if the designated session is still + * alive or not.</p> + * + * @param sid the identifier of the session to check. + * @return <code>true</code> if the designated session is still alive. + * <code>false</code> otherwise. + */ + boolean isAlive(final byte[] sid) + { + boolean result = false; + if (sid != null && sid.length != 0) + { + synchronized (lock) + { + final String key = new String(sid); + final StoreEntry ctx = (StoreEntry) sid2ttl.get(key); + if (ctx != null) + { + result = ctx.isAlive(); + if (!result) + { // invalidate it en-passant + sid2ssc.remove(key); + sid2ttl.remove(key); + } + } + } + } + return result; + } + + /** + * <p>Records a mapping between a session identifier and the Security Context + * of the designated SRP server mechanism instance.</p> + * + * @param ttl the session's Time-To-Live indicator (in seconds). + * @param ctx the server's security context. + */ + void cacheSession(final int ttl, final SecurityContext ctx) + { + synchronized (lock) + { + final String key = new String(ctx.getSID()); + sid2ssc.put(key, ctx); + sid2ttl.put(key, new StoreEntry(ttl)); + } + } + + /** + * <p>Updates the mapping between the designated session identifier and the + * designated server's SASL Security Context. In the process, computes + * and return the underlying mechanism server's evidence that shall be + * returned to the client in a session re-use exchange.</p> + * + * @param sid the identifier of the session to restore. + * @return an SRP server's security context. + */ + SecurityContext restoreSession(final byte[] sid) + { + final String key = new String(sid); + final SecurityContext result; + synchronized (lock) + { + result = (SecurityContext) sid2ssc.remove(key); + sid2ttl.remove(key); + } + return result; + } + + /** + * <p>Removes all information related to the designated session ID.</p> + * + * @param sid the identifier of the seesion to invalidate. + */ + void invalidateSession(final byte[] sid) + { + final String key = new String(sid); + synchronized (lock) + { + sid2ssc.remove(key); + sid2ttl.remove(key); + } + } +}
\ No newline at end of file diff --git a/libjava/classpath/gnu/javax/crypto/sasl/srp/StoreEntry.java b/libjava/classpath/gnu/javax/crypto/sasl/srp/StoreEntry.java new file mode 100644 index 00000000000..c5041fa4b38 --- /dev/null +++ b/libjava/classpath/gnu/javax/crypto/sasl/srp/StoreEntry.java @@ -0,0 +1,89 @@ +/* StoreEntry.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a 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 of the License, 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; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, 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.javax.crypto.sasl.srp; + +/** + * <p>A simple timing-related object for use by SRP re-use code.</p> + */ +class StoreEntry +{ + + // Constants and variables + // ------------------------------------------------------------------------- + + private boolean perenial; + + private long timeToDie; + + // Constructor(s) + // ------------------------------------------------------------------------- + + StoreEntry(int ttl) + { + super(); + + if (ttl == 0) + { + perenial = true; + timeToDie = 0L; + } + else + { + perenial = false; + timeToDie = System.currentTimeMillis() + (ttl & 0xFFFFFFFFL) * 1000L; + } + } + + // Class methods + // ------------------------------------------------------------------------- + + // Instance methods + // ------------------------------------------------------------------------- + + /** + * <p>Returns <code>true</code> if the Time-To_live period has not elapsed.</p> + * + * @return <code>true</code> if the Time-To-Live period (in seconds) has not + * elapsed yet; <code>false</code> otherwise. + */ + boolean isAlive() + { + return (perenial ? true : (System.currentTimeMillis() < timeToDie)); + } +}
\ No newline at end of file |

