package gnu.crypto.sasl.srp;
// ----------------------------------------------------------------------------
// $Id: SRP.java,v 1.6 2005/10/06 04:24:18 rsdio Exp $
//
// Copyright (C) 2003 Free Software Foundation, Inc.
//
// This file is part of GNU Crypto.
//
// GNU Crypto is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2, or (at your option)
// any later version.
//
// GNU Crypto 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 this program; see the file COPYING. If not, write to the
//
// Free Software Foundation Inc.,
// 51 Franklin Street, Fifth Floor,
// Boston, MA 02110-1301
// USA
//
// Linking this library statically or dynamically with other modules is
// making a combined work based on this library. Thus, the terms and
// conditions of the GNU General Public License cover the whole
// combination.
//
// As a special exception, the copyright holders of this library give
// you permission to link this library with independent modules to
// produce an executable, regardless of the license terms of these
// independent modules, and to copy and distribute the resulting
// executable under terms of your choice, provided that you also meet,
// for each linked independent module, the terms and conditions of the
// license of that module. An independent module is a module which is
// not derived from or based on this library. If you modify this
// library, you may extend this exception to your version of the
// library, but you are not obligated to do so. If you do not wish to
// do so, delete this exception statement from your version.
// ----------------------------------------------------------------------------
import gnu.crypto.hash.HashFactory;
import gnu.crypto.hash.IMessageDigest;
import gnu.crypto.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>
*
* @version $Revision: 1.6 $
*/
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();
}
}