/* This code is part of Freenet. It is distributed under the GNU General * Public License, version 2 (or at your option any later version). See * http://www.gnu.org/ for further details of the GPL. */ package freenet.crypt; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.math.BigInteger; import java.util.Random; import java.util.Vector; import net.i2p.util.NativeBigInteger; import freenet.support.Base64; import freenet.support.HexUtil; import freenet.support.IllegalBase64Exception; //import freenet.support.SimpleFieldSet; /** * Holds DSA group parameters. These are the public (possibly shared) values * needed for the DSA algorithm */ public class DSAGroup extends CryptoKey { private static final long serialVersionUID = -1; public static final int Q_BIT_LENGTH = 256; private final BigInteger p, q, g; // of the // hexadecimal // string // representations // of p,q and g public DSAGroup(BigInteger p, BigInteger q, BigInteger g) { this.p = p; this.q = q; this.g = g; if(p.signum() != 1 || q.signum() != 1 || g.signum() != 1) throw new IllegalArgumentException(); } public DSAGroup(String pAsHexString, String qAsHexString, String gAsHexString) throws NumberFormatException { //Sanity check. Needed because of the Kaffe workaround further down if ((pAsHexString == null) || (qAsHexString == null) || (gAsHexString == null)) throw new NullPointerException("Invalid DSAGroup"); try { this.p = new NativeBigInteger(pAsHexString, 16); this.q = new NativeBigInteger(qAsHexString, 16); this.g = new NativeBigInteger(gAsHexString, 16); } catch (NullPointerException e) { // yea, i know, don't catch NPEs .. but _some_ JVMs don't // throw the NFE like they are supposed to (*cough* kaffe) throw new NumberFormatException(e + " while converting " + pAsHexString + ',' + qAsHexString + " and " + gAsHexString + " to integers"); } if(p.signum() != 1 || q.signum() != 1 || g.signum() != 1) throw new IllegalArgumentException(); } /** * Parses a DSA Group from a string, where p, q, and g are in unsigned * hex-strings, separated by a commas */ // see readFromField() below //public static DSAGroup parse(String grp) { // StringTokenizer str=new StringTokenizer(grp, ","); // BigInteger p,q,g; // p = new BigInteger(str.nextToken(), 16); // q = new BigInteger(str.nextToken(), 16); // g = new BigInteger(str.nextToken(), 16); // return new DSAGroup(p,q,g); //} public static CryptoKey read(InputStream i) throws IOException, CryptFormatException { BigInteger p, q, g; p = Util.readMPI(i); q = Util.readMPI(i); g = Util.readMPI(i); try { return new DSAGroup(p, q, g); } catch (IllegalArgumentException e) { CryptFormatException ce = new CryptFormatException("Invalid group: "+e); ce.initCause(e); throw ce; } } public void writeForWire(OutputStream out) throws IOException { Util.writeMPI(p, out); Util.writeMPI(q, out); Util.writeMPI(g, out); } // public void write(OutputStream out) throws IOException { // write(out, getClass().getName()); // writeForWire(out); // } public String keyType() { return "DSA.g-" + p.bitLength(); } public BigInteger getP() { return p; } public BigInteger getQ() { return q; } public BigInteger getG() { return g; } public byte[] fingerprint() { BigInteger fp[] = new BigInteger[3]; fp[0] = p; fp[1] = q; fp[2] = g; return fingerprint(fp); } static class QG extends Thread { public Vector qs = new Vector(); protected Random r; public QG(Random r) { setDaemon(true); this.r = r; } public void run() { while (true) { qs.addElement(makePrime(DSAGroup.Q_BIT_LENGTH, 80, r)); synchronized (this) { notifyAll(); } while (qs.size() >= 3) { synchronized (this) { try { wait(50); } catch (InterruptedException ie) { } } } } } } static BigInteger smallPrimes[] = new BigInteger[] { BigInteger.valueOf(3), BigInteger.valueOf(5), BigInteger.valueOf(7), BigInteger.valueOf(11), BigInteger.valueOf(13), BigInteger.valueOf(17), BigInteger.valueOf(19), BigInteger.valueOf(23), BigInteger.valueOf(29)}; public static BigInteger makePrime(int bits, int confidence, Random r) { BigInteger rv; do { // FIXME: is this likely to get modPow()ed? // I don't suppose it matters, there isn't much overhead rv = new NativeBigInteger(bits, r).setBit(0).setBit(bits - 1); } while (!isPrime(rv, confidence)); return rv; } public static boolean isPrime(BigInteger b, int confidence) { for (int i = 0; i < smallPrimes.length; i++) { if (b.mod(smallPrimes[i]).equals(BigInteger.ZERO)) return false; } return b.isProbablePrime(80); } public byte[] asBytes() { byte[] pb = Util.MPIbytes(p); byte[] qb = Util.MPIbytes(q); byte[] gb = Util.MPIbytes(g); byte[] tb = new byte[pb.length + qb.length + gb.length]; System.arraycopy(pb, 0, tb, 0, pb.length); System.arraycopy(qb, 0, tb, pb.length, qb.length); System.arraycopy(gb, 0, tb, pb.length + qb.length, gb.length); return tb; } public boolean equals(Object o) { if (this == o) // Not necessary, but a very cheap optimization return true; return (o instanceof DSAGroup) && p.equals(((DSAGroup) o).p) && q.equals(((DSAGroup) o).q) && g.equals(((DSAGroup) o).g); } public boolean equals(DSAGroup o) { if (this == o) // Not necessary, but a very cheap optimization return true; return p.equals(o.p) && q.equals(o.q) && g.equals(o.g); } public int hashCode() { return p.hashCode() ^ q.hashCode() ^ g.hashCode(); } public SimpleFieldSet asFieldSet() { SimpleFieldSet fs = new SimpleFieldSet(true); fs.putSingle("p", Base64.encode(p.toByteArray())); fs.putSingle("q", Base64.encode(q.toByteArray())); fs.putSingle("g", Base64.encode(g.toByteArray())); return fs; } public static DSAGroup create(SimpleFieldSet fs) throws IllegalBase64Exception { BigInteger p = new NativeBigInteger(1, Base64.decode(fs.get("p"))); BigInteger q = new NativeBigInteger(1, Base64.decode(fs.get("q"))); BigInteger g = new NativeBigInteger(1, Base64.decode(fs.get("g"))); DSAGroup dg = new DSAGroup(p, q, g); if(dg.equals(Global.DSAgroupBigA)) return Global.DSAgroupBigA; return dg; } public String toString() { if(this == Global.DSAgroupBigA) return "Global.DSAgroupBigA"; else return super.toString(); } public String toLongString() { if(this == Global.DSAgroupBigA) return "Global.DSAgroupBigA"; return "p="+HexUtil.biToHex(p)+", q="+HexUtil.biToHex(q)+", g="+HexUtil.biToHex(g); } }