package ssh;

import java.math.*;
import java.io.*;

/**
 * Tools used to work with BigIntegers on DataInput and DataOutput
 * streams.
 */
public class StreamExtras {

	/**
	 * Writes a BigInteger to the stream in the form specified
	 * by the ssh 1.5 internet-draft.
	 */
	public static void writeBigInteger(DataOutput dos, BigInteger bi)
		throws IOException {

		int bits = bi.bitLength();
		dos.writeShort((short) bits);

		// Compute number of bytes to send based on the bits field.
		// (draft-ylonen, page 7.) This is important because Java's
		// BigInteger class sometimes prepends a 0-filled most-significant
		// byte; we don't want to send that if we didn't account for
		// it in the bits field.
		//
		// An alternative would be to send bits=mpData.length*8,
		// and send those extra zeros. We don't do that, because when
		// we convert a BigInteger to a byte array for the MD5 step of
		// the sessionID computation, "the modulus of the host key is
		// interpreted as a byte string (without explicit length field,
		// with the minimum length able to hold the whole value)."
		// (Actually, that computation doesn't use this method, but
		// that makes the code a little more consistent.)
		int bytes = (bits+7)/8;		// draft-ylonen page 7
		byte[] data = bi.toByteArray();
		dos.write(data, data.length-bytes, bytes);
	}

	/**
	 * Reads a BigInteger from the stream in the form specified
	 * by the ssh 1.5 internet-draft.
	 */
	public static BigInteger readBigInteger(DataInput dis)
		throws IOException {

		BigInteger bi = new BigInteger("0");
		int bits = dis.readShort();
		int bytes = (bits + 7) / 8;		// per page 7 of draft
		// The draft doesn't mention anything about negative values. Gulp?

		byte[] mpData = new byte[bytes];
		dis.readFully(mpData, 0, bytes);
		for (int i=0; i<bytes; i++) {
			bi = bi.shiftLeft(8);	// shift out existing bits
			byte[] thisVal = new byte[2];
			thisVal[0] = 0;				// ensures val is positive?
			thisVal[1] = mpData[i];

			BigInteger op2 = new BigInteger(thisVal);
			bi = bi.or(op2);			// or in latest 8 bits
		}

		return bi;
	}
}
