0N/A/*
2362N/A * Copyright (c) 1996, 2004, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage sun.security.provider;
0N/A
0N/Aimport java.io.*;
0N/Aimport java.util.*;
0N/Aimport java.math.BigInteger;
0N/Aimport java.nio.ByteBuffer;
0N/A
0N/Aimport java.security.*;
0N/Aimport java.security.SecureRandom;
0N/Aimport java.security.interfaces.*;
0N/Aimport java.security.spec.DSAParameterSpec;
0N/Aimport java.security.spec.InvalidParameterSpecException;
0N/A
0N/Aimport sun.security.util.Debug;
0N/Aimport sun.security.util.DerValue;
0N/Aimport sun.security.util.DerInputStream;
0N/Aimport sun.security.util.DerOutputStream;
0N/Aimport sun.security.x509.AlgIdDSA;
0N/Aimport sun.security.jca.JCAUtil;
0N/A
0N/A/**
0N/A * The Digital Signature Standard (using the Digital Signature
0N/A * Algorithm), as described in fips186 of the National Instute of
0N/A * Standards and Technology (NIST), using fips180-1 (SHA-1).
0N/A *
0N/A * This file contains both the signature implementation for the
0N/A * commonly used SHA1withDSA (DSS) as well as RawDSA, used by TLS
0N/A * among others. RawDSA expects the 20 byte SHA-1 digest as input
0N/A * via update rather than the original data like other signature
0N/A * implementations.
0N/A *
0N/A * @author Benjamin Renaud
0N/A *
0N/A * @since 1.1
0N/A *
0N/A * @see DSAPublicKey
0N/A * @see DSAPrivateKey
0N/A */
0N/Aabstract class DSA extends SignatureSpi {
0N/A
0N/A /* Are we debugging? */
0N/A private static final boolean debug = false;
0N/A
0N/A /* The parameter object */
0N/A private DSAParams params;
0N/A
0N/A /* algorithm parameters */
0N/A private BigInteger presetP, presetQ, presetG;
0N/A
0N/A /* The public key, if any */
0N/A private BigInteger presetY;
0N/A
0N/A /* The private key, if any */
0N/A private BigInteger presetX;
0N/A
0N/A /* The random seed used to generate k */
0N/A private int[] Kseed;
0N/A
0N/A /* The random seed used to generate k (specified by application) */
0N/A private byte[] KseedAsByteArray;
0N/A
0N/A /*
0N/A * The random seed used to generate k
0N/A * (prevent the same Kseed from being used twice in a row
0N/A */
0N/A private int[] previousKseed;
0N/A
0N/A /* The RNG used to output a seed for generating k */
0N/A private SecureRandom signingRandom;
0N/A
0N/A /**
0N/A * Construct a blank DSA object. It must be
0N/A * initialized before being usable for signing or verifying.
0N/A */
0N/A DSA() {
0N/A super();
0N/A }
0N/A
0N/A /**
0N/A * Return the 20 byte hash value and reset the digest.
0N/A */
0N/A abstract byte[] getDigest() throws SignatureException;
0N/A
0N/A /**
0N/A * Reset the digest.
0N/A */
0N/A abstract void resetDigest();
0N/A
0N/A /**
0N/A * Standard SHA1withDSA implementation.
0N/A */
0N/A public static final class SHA1withDSA extends DSA {
0N/A
0N/A /* The SHA hash for the data */
0N/A private final MessageDigest dataSHA;
0N/A
0N/A public SHA1withDSA() throws NoSuchAlgorithmException {
0N/A dataSHA = MessageDigest.getInstance("SHA-1");
0N/A }
0N/A
0N/A /**
0N/A * Update a byte to be signed or verified.
0N/A */
0N/A protected void engineUpdate(byte b) {
0N/A dataSHA.update(b);
0N/A }
0N/A
0N/A /**
0N/A * Update an array of bytes to be signed or verified.
0N/A */
0N/A protected void engineUpdate(byte[] data, int off, int len) {
0N/A dataSHA.update(data, off, len);
0N/A }
0N/A
0N/A protected void engineUpdate(ByteBuffer b) {
0N/A dataSHA.update(b);
0N/A }
0N/A
0N/A byte[] getDigest() {
0N/A return dataSHA.digest();
0N/A }
0N/A
0N/A void resetDigest() {
0N/A dataSHA.reset();
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * RawDSA implementation.
0N/A *
0N/A * RawDSA requires the data to be exactly 20 bytes long. If it is
0N/A * not, a SignatureException is thrown when sign()/verify() is called
0N/A * per JCA spec.
0N/A */
0N/A public static final class RawDSA extends DSA {
0N/A
0N/A // length of the SHA-1 digest (20 bytes)
0N/A private final static int SHA1_LEN = 20;
0N/A
0N/A // 20 byte digest buffer
0N/A private final byte[] digestBuffer;
0N/A
0N/A // offset into the buffer
0N/A private int ofs;
0N/A
0N/A public RawDSA() {
0N/A digestBuffer = new byte[SHA1_LEN];
0N/A }
0N/A
0N/A protected void engineUpdate(byte b) {
0N/A if (ofs == SHA1_LEN) {
0N/A ofs = SHA1_LEN + 1;
0N/A return;
0N/A }
0N/A digestBuffer[ofs++] = b;
0N/A }
0N/A
0N/A protected void engineUpdate(byte[] data, int off, int len) {
0N/A if (ofs + len > SHA1_LEN) {
0N/A ofs = SHA1_LEN + 1;
0N/A return;
0N/A }
0N/A System.arraycopy(data, off, digestBuffer, ofs, len);
0N/A ofs += len;
0N/A }
0N/A
0N/A byte[] getDigest() throws SignatureException {
0N/A if (ofs != SHA1_LEN) {
0N/A throw new SignatureException
0N/A ("Data for RawDSA must be exactly 20 bytes long");
0N/A }
0N/A ofs = 0;
0N/A return digestBuffer;
0N/A }
0N/A
0N/A void resetDigest() {
0N/A ofs = 0;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Initialize the DSA object with a DSA private key.
0N/A *
0N/A * @param privateKey the DSA private key
0N/A *
0N/A * @exception InvalidKeyException if the key is not a valid DSA private
0N/A * key.
0N/A */
0N/A protected void engineInitSign(PrivateKey privateKey)
0N/A throws InvalidKeyException {
0N/A if (!(privateKey instanceof java.security.interfaces.DSAPrivateKey)) {
0N/A throw new InvalidKeyException("not a DSA private key: " +
0N/A privateKey);
0N/A }
0N/A java.security.interfaces.DSAPrivateKey priv =
0N/A (java.security.interfaces.DSAPrivateKey)privateKey;
0N/A this.presetX = priv.getX();
0N/A this.presetY = null;
0N/A initialize(priv.getParams());
0N/A }
0N/A
0N/A /**
0N/A * Initialize the DSA object with a DSA public key.
0N/A *
0N/A * @param publicKey the DSA public key.
0N/A *
0N/A * @exception InvalidKeyException if the key is not a valid DSA public
0N/A * key.
0N/A */
0N/A protected void engineInitVerify(PublicKey publicKey)
0N/A throws InvalidKeyException {
0N/A if (!(publicKey instanceof java.security.interfaces.DSAPublicKey)) {
0N/A throw new InvalidKeyException("not a DSA public key: " +
0N/A publicKey);
0N/A }
0N/A java.security.interfaces.DSAPublicKey pub =
0N/A (java.security.interfaces.DSAPublicKey)publicKey;
0N/A this.presetY = pub.getY();
0N/A this.presetX = null;
0N/A initialize(pub.getParams());
0N/A }
0N/A
0N/A private void initialize(DSAParams params) throws InvalidKeyException {
0N/A resetDigest();
0N/A setParams(params);
0N/A }
0N/A
0N/A /**
0N/A * Sign all the data thus far updated. The signature is formatted
0N/A * according to the Canonical Encoding Rules, returned as a DER
0N/A * sequence of Integer, r and s.
0N/A *
0N/A * @return a signature block formatted according to the Canonical
0N/A * Encoding Rules.
0N/A *
0N/A * @exception SignatureException if the signature object was not
0N/A * properly initialized, or if another exception occurs.
0N/A *
0N/A * @see sun.security.DSA#engineUpdate
0N/A * @see sun.security.DSA#engineVerify
0N/A */
0N/A protected byte[] engineSign() throws SignatureException {
0N/A BigInteger k = generateK(presetQ);
0N/A BigInteger r = generateR(presetP, presetQ, presetG, k);
0N/A BigInteger s = generateS(presetX, presetQ, r, k);
0N/A
0N/A try {
0N/A DerOutputStream outseq = new DerOutputStream(100);
0N/A outseq.putInteger(r);
0N/A outseq.putInteger(s);
0N/A DerValue result = new DerValue(DerValue.tag_Sequence,
0N/A outseq.toByteArray());
0N/A
0N/A return result.toByteArray();
0N/A
0N/A } catch (IOException e) {
0N/A throw new SignatureException("error encoding signature");
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Verify all the data thus far updated.
0N/A *
0N/A * @param signature the alledged signature, encoded using the
0N/A * Canonical Encoding Rules, as a sequence of integers, r and s.
0N/A *
0N/A * @exception SignatureException if the signature object was not
0N/A * properly initialized, or if another exception occurs.
0N/A *
0N/A * @see sun.security.DSA#engineUpdate
0N/A * @see sun.security.DSA#engineSign
0N/A */
0N/A protected boolean engineVerify(byte[] signature)
0N/A throws SignatureException {
0N/A return engineVerify(signature, 0, signature.length);
0N/A }
0N/A
0N/A /**
0N/A * Verify all the data thus far updated.
0N/A *
0N/A * @param signature the alledged signature, encoded using the
0N/A * Canonical Encoding Rules, as a sequence of integers, r and s.
0N/A *
0N/A * @param offset the offset to start from in the array of bytes.
0N/A *
0N/A * @param length the number of bytes to use, starting at offset.
0N/A *
0N/A * @exception SignatureException if the signature object was not
0N/A * properly initialized, or if another exception occurs.
0N/A *
0N/A * @see sun.security.DSA#engineUpdate
0N/A * @see sun.security.DSA#engineSign
0N/A */
0N/A protected boolean engineVerify(byte[] signature, int offset, int length)
0N/A throws SignatureException {
0N/A
0N/A BigInteger r = null;
0N/A BigInteger s = null;
0N/A // first decode the signature.
0N/A try {
0N/A DerInputStream in = new DerInputStream(signature, offset, length);
0N/A DerValue[] values = in.getSequence(2);
0N/A
0N/A r = values[0].getBigInteger();
0N/A s = values[1].getBigInteger();
0N/A
0N/A } catch (IOException e) {
0N/A throw new SignatureException("invalid encoding for signature");
0N/A }
0N/A
0N/A // some implementations do not correctly encode values in the ASN.1
0N/A // 2's complement format. force r and s to be positive in order to
0N/A // to validate those signatures
0N/A if (r.signum() < 0) {
0N/A r = new BigInteger(1, r.toByteArray());
0N/A }
0N/A if (s.signum() < 0) {
0N/A s = new BigInteger(1, s.toByteArray());
0N/A }
0N/A
0N/A if ((r.compareTo(presetQ) == -1) && (s.compareTo(presetQ) == -1)) {
0N/A BigInteger w = generateW(presetP, presetQ, presetG, s);
0N/A BigInteger v = generateV(presetY, presetP, presetQ, presetG, w, r);
0N/A return v.equals(r);
0N/A } else {
0N/A throw new SignatureException("invalid signature: out of range values");
0N/A }
0N/A }
0N/A
0N/A private BigInteger generateR(BigInteger p, BigInteger q, BigInteger g,
0N/A BigInteger k) {
0N/A BigInteger temp = g.modPow(k, p);
0N/A return temp.remainder(q);
0N/A }
0N/A
0N/A private BigInteger generateS(BigInteger x, BigInteger q,
0N/A BigInteger r, BigInteger k) throws SignatureException {
0N/A
0N/A byte[] s2 = getDigest();
0N/A BigInteger temp = new BigInteger(1, s2);
0N/A BigInteger k1 = k.modInverse(q);
0N/A
0N/A BigInteger s = x.multiply(r);
0N/A s = temp.add(s);
0N/A s = k1.multiply(s);
0N/A return s.remainder(q);
0N/A }
0N/A
0N/A private BigInteger generateW(BigInteger p, BigInteger q,
0N/A BigInteger g, BigInteger s) {
0N/A return s.modInverse(q);
0N/A }
0N/A
0N/A private BigInteger generateV(BigInteger y, BigInteger p,
0N/A BigInteger q, BigInteger g, BigInteger w, BigInteger r)
0N/A throws SignatureException {
0N/A
0N/A byte[] s2 = getDigest();
0N/A BigInteger temp = new BigInteger(1, s2);
0N/A
0N/A temp = temp.multiply(w);
0N/A BigInteger u1 = temp.remainder(q);
0N/A
0N/A BigInteger u2 = (r.multiply(w)).remainder(q);
0N/A
0N/A BigInteger t1 = g.modPow(u1,p);
0N/A BigInteger t2 = y.modPow(u2,p);
0N/A BigInteger t3 = t1.multiply(t2);
0N/A BigInteger t5 = t3.remainder(p);
0N/A return t5.remainder(q);
0N/A }
0N/A
0N/A /*
0N/A * Please read bug report 4044247 for an alternative, faster,
0N/A * NON-FIPS approved method to generate K
0N/A */
0N/A private BigInteger generateK(BigInteger q) {
0N/A
0N/A BigInteger k = null;
0N/A
0N/A // The application specified a Kseed for us to use.
0N/A // Note that we do not allow usage of the same Kseed twice in a row
0N/A if (Kseed != null && !Arrays.equals(Kseed, previousKseed)) {
0N/A k = generateK(Kseed, q);
0N/A if (k.signum() > 0 && k.compareTo(q) < 0) {
0N/A previousKseed = new int [Kseed.length];
0N/A System.arraycopy(Kseed, 0, previousKseed, 0, Kseed.length);
0N/A return k;
0N/A }
0N/A }
0N/A
0N/A // The application did not specify a Kseed for us to use.
0N/A // We'll generate a new Kseed by getting random bytes from
0N/A // a SecureRandom object.
0N/A SecureRandom random = getSigningRandom();
0N/A
0N/A while (true) {
0N/A int[] seed = new int[5];
0N/A
0N/A for (int i = 0; i < 5; i++)
0N/A seed[i] = random.nextInt();
0N/A k = generateK(seed, q);
0N/A if (k.signum() > 0 && k.compareTo(q) < 0) {
0N/A previousKseed = new int [seed.length];
0N/A System.arraycopy(seed, 0, previousKseed, 0, seed.length);
0N/A return k;
0N/A }
0N/A }
0N/A }
0N/A
0N/A // Use the application-specified SecureRandom Object if provided.
0N/A // Otherwise, use our default SecureRandom Object.
0N/A private SecureRandom getSigningRandom() {
0N/A if (signingRandom == null) {
0N/A if (appRandom != null) {
0N/A signingRandom = appRandom;
0N/A } else {
0N/A signingRandom = JCAUtil.getSecureRandom();
0N/A }
0N/A }
0N/A return signingRandom;
0N/A }
0N/A
0N/A /**
0N/A * Compute k for a DSA signature.
0N/A *
0N/A * @param seed the seed for generating k. This seed should be
0N/A * secure. This is what is refered to as the KSEED in the DSA
0N/A * specification.
0N/A *
0N/A * @param g the g parameter from the DSA key pair.
0N/A */
0N/A private BigInteger generateK(int[] seed, BigInteger q) {
0N/A
0N/A // check out t in the spec.
0N/A int[] t = { 0xEFCDAB89, 0x98BADCFE, 0x10325476,
0N/A 0xC3D2E1F0, 0x67452301 };
0N/A //
0N/A int[] tmp = DSA.SHA_7(seed, t);
0N/A byte[] tmpBytes = new byte[tmp.length * 4];
0N/A for (int i = 0; i < tmp.length; i++) {
0N/A int k = tmp[i];
0N/A for (int j = 0; j < 4; j++) {
0N/A tmpBytes[(i * 4) + j] = (byte) (k >>> (24 - (j * 8)));
0N/A }
0N/A }
0N/A BigInteger k = new BigInteger(1, tmpBytes).mod(q);
0N/A return k;
0N/A }
0N/A
0N/A // Constants for each round
0N/A private static final int round1_kt = 0x5a827999;
0N/A private static final int round2_kt = 0x6ed9eba1;
0N/A private static final int round3_kt = 0x8f1bbcdc;
0N/A private static final int round4_kt = 0xca62c1d6;
0N/A
0N/A /**
0N/A * Computes set 1 thru 7 of SHA-1 on m1. */
0N/A static int[] SHA_7(int[] m1, int[] h) {
0N/A
0N/A int[] W = new int[80];
0N/A System.arraycopy(m1,0,W,0,m1.length);
0N/A int temp = 0;
0N/A
0N/A for (int t = 16; t <= 79; t++){
0N/A temp = W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16];
0N/A W[t] = ((temp << 1) | (temp >>>(32 - 1)));
0N/A }
0N/A
0N/A int a = h[0],b = h[1],c = h[2], d = h[3], e = h[4];
0N/A for (int i = 0; i < 20; i++) {
0N/A temp = ((a<<5) | (a>>>(32-5))) +
0N/A ((b&c)|((~b)&d))+ e + W[i] + round1_kt;
0N/A e = d;
0N/A d = c;
0N/A c = ((b<<30) | (b>>>(32-30)));
0N/A b = a;
0N/A a = temp;
0N/A }
0N/A
0N/A // Round 2
0N/A for (int i = 20; i < 40; i++) {
0N/A temp = ((a<<5) | (a>>>(32-5))) +
0N/A (b ^ c ^ d) + e + W[i] + round2_kt;
0N/A e = d;
0N/A d = c;
0N/A c = ((b<<30) | (b>>>(32-30)));
0N/A b = a;
0N/A a = temp;
0N/A }
0N/A
0N/A // Round 3
0N/A for (int i = 40; i < 60; i++) {
0N/A temp = ((a<<5) | (a>>>(32-5))) +
0N/A ((b&c)|(b&d)|(c&d)) + e + W[i] + round3_kt;
0N/A e = d;
0N/A d = c;
0N/A c = ((b<<30) | (b>>>(32-30)));
0N/A b = a;
0N/A a = temp;
0N/A }
0N/A
0N/A // Round 4
0N/A for (int i = 60; i < 80; i++) {
0N/A temp = ((a<<5) | (a>>>(32-5))) +
0N/A (b ^ c ^ d) + e + W[i] + round4_kt;
0N/A e = d;
0N/A d = c;
0N/A c = ((b<<30) | (b>>>(32-30)));
0N/A b = a;
0N/A a = temp;
0N/A }
0N/A int[] md = new int[5];
0N/A md[0] = h[0] + a;
0N/A md[1] = h[1] + b;
0N/A md[2] = h[2] + c;
0N/A md[3] = h[3] + d;
0N/A md[4] = h[4] + e;
0N/A return md;
0N/A }
0N/A
0N/A
0N/A /**
0N/A * This implementation recognizes the following parameter:<dl>
0N/A *
0N/A * <dt><tt>Kseed</tt>
0N/A *
0N/A * <dd>a byte array.
0N/A *
0N/A * </dl>
0N/A *
0N/A * @deprecated
0N/A */
0N/A @Deprecated
0N/A protected void engineSetParameter(String key, Object param) {
0N/A if (key.equals("KSEED")) {
0N/A if (param instanceof byte[]) {
0N/A Kseed = byteArray2IntArray((byte[])param);
0N/A KseedAsByteArray = (byte[])param;
0N/A } else {
0N/A debug("unrecognized param: " + key);
0N/A throw new InvalidParameterException("Kseed not a byte array");
0N/A }
0N/A } else {
0N/A throw new InvalidParameterException("invalid parameter");
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Return the value of the requested parameter. Recognized
0N/A * parameters are:
0N/A *
0N/A * <dl>
0N/A *
0N/A * <dt><tt>Kseed</tt>
0N/A *
0N/A * <dd>a byte array.
0N/A *
0N/A * </dl>
0N/A *
0N/A * @return the value of the requested parameter.
0N/A *
0N/A * @see java.security.SignatureEngine
0N/A *
0N/A * @deprecated
0N/A */
0N/A @Deprecated
0N/A protected Object engineGetParameter(String key) {
0N/A if (key.equals("KSEED")) {
0N/A return KseedAsByteArray;
0N/A } else {
0N/A return null;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Set the algorithm object.
0N/A */
0N/A private void setParams(DSAParams params) throws InvalidKeyException {
0N/A if (params == null) {
0N/A throw new InvalidKeyException("DSA public key lacks parameters");
0N/A }
0N/A this.params = params;
0N/A this.presetP = params.getP();
0N/A this.presetQ = params.getQ();
0N/A this.presetG = params.getG();
0N/A }
0N/A
0N/A /**
0N/A * Return a human readable rendition of the engine.
0N/A */
0N/A public String toString() {
0N/A String printable = "DSA Signature";
0N/A if (presetP != null && presetQ != null && presetG != null) {
0N/A printable += "\n\tp: " + Debug.toHexString(presetP);
0N/A printable += "\n\tq: " + Debug.toHexString(presetQ);
0N/A printable += "\n\tg: " + Debug.toHexString(presetG);
0N/A } else {
0N/A printable += "\n\t P, Q or G not initialized.";
0N/A }
0N/A if (presetY != null) {
0N/A printable += "\n\ty: " + Debug.toHexString(presetY);
0N/A }
0N/A if (presetY == null && presetX == null) {
0N/A printable += "\n\tUNINIIALIZED";
0N/A }
0N/A return printable;
0N/A }
0N/A
0N/A /*
0N/A * Utility routine for converting a byte array into an int array
0N/A */
0N/A private int[] byteArray2IntArray(byte[] byteArray) {
0N/A
0N/A int j = 0;
0N/A byte[] newBA;
0N/A int mod = byteArray.length % 4;
0N/A
0N/A // guarantee that the incoming byteArray is a multiple of 4
0N/A // (pad with 0's)
0N/A switch (mod) {
0N/A case 3: newBA = new byte[byteArray.length + 1]; break;
0N/A case 2: newBA = new byte[byteArray.length + 2]; break;
0N/A case 1: newBA = new byte[byteArray.length + 3]; break;
0N/A default: newBA = new byte[byteArray.length + 0]; break;
0N/A }
0N/A System.arraycopy(byteArray, 0, newBA, 0, byteArray.length);
0N/A
0N/A // copy each set of 4 bytes in the byte array into an integer
0N/A int[] newSeed = new int[newBA.length / 4];
0N/A for (int i = 0; i < newBA.length; i += 4) {
0N/A newSeed[j] = newBA[i + 3] & 0xFF;
0N/A newSeed[j] |= (newBA[i + 2] << 8) & 0xFF00;
0N/A newSeed[j] |= (newBA[i + 1] << 16) & 0xFF0000;
0N/A newSeed[j] |= (newBA[i + 0] << 24) & 0xFF000000;
0N/A j++;
0N/A }
0N/A
0N/A return newSeed;
0N/A }
0N/A
0N/A private static void debug(Exception e) {
0N/A if (debug) {
0N/A e.printStackTrace();
0N/A }
0N/A }
0N/A
0N/A private static void debug(String s) {
0N/A if (debug) {
0N/A System.err.println(s);
0N/A }
0N/A }
0N/A}