package net.java.otr4j.io;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigInteger;
import java.security.PublicKey;
import java.security.interfaces.DSAParams;
import java.security.interfaces.DSAPublicKey;
import javax.crypto.interfaces.DHPublicKey;
import net.java.otr4j.io.messages.MysteriousT;
import net.java.otr4j.io.messages.SignatureM;
import net.java.otr4j.io.messages.SignatureX;
import org.spongycastle.util.BigIntegers;
public class OtrOutputStream extends FilterOutputStream implements SerializationConstants {
public OtrOutputStream(OutputStream out) {
super(out);
}
private void writeNumber(int value, int length) throws IOException {
byte[] b = new byte[length];
for (int i = 0; i < length; i++) {
int offset = (b.length - 1 - i) * 8;
b[i] = (byte) ((value >>> offset) & 0xFF);
}
write(b);
}
public void writeBigInt(BigInteger bi) throws IOException {
byte[] b = BigIntegers.asUnsignedByteArray(bi);
writeData(b);
}
public void writeByte(int b) throws IOException {
writeNumber(b, TYPE_LEN_BYTE);
}
public void writeData(byte[] b) throws IOException {
int len = (b == null || b.length < 0) ? 0 : b.length;
writeNumber(len, DATA_LEN);
if (len > 0)
write(b);
}
public void writeInt(int i) throws IOException {
writeNumber(i, TYPE_LEN_INT);
}
public void writeShort(int s) throws IOException {
writeNumber(s, TYPE_LEN_SHORT);
}
public void writeMac(byte[] mac) throws IOException {
if (mac == null || mac.length != TYPE_LEN_MAC)
throw new IllegalArgumentException();
write(mac);
}
public void writeCtr(byte[] ctr) throws IOException {
if (ctr == null || ctr.length < 1)
return;
int i = 0;
while (i < TYPE_LEN_CTR && i < ctr.length) {
write(ctr[i]);
i++;
}
}
public void writeDHPublicKey(DHPublicKey dhPublicKey) throws IOException {
byte[] b = BigIntegers.asUnsignedByteArray(dhPublicKey.getY());
writeData(b);
}
public void writePublicKey(PublicKey pubKey) throws IOException {
if (!(pubKey instanceof DSAPublicKey))
throw new UnsupportedOperationException(
"Key types other than DSA are not supported at the moment.");
DSAPublicKey dsaKey = (DSAPublicKey) pubKey;
writeShort(0);
DSAParams dsaParams = dsaKey.getParams();
writeBigInt(dsaParams.getP());
writeBigInt(dsaParams.getQ());
writeBigInt(dsaParams.getG());
writeBigInt(dsaKey.getY());
}
public void writeTlvData(byte[] b) throws IOException {
int len = (b == null || b.length < 0) ? 0 : b.length;
writeNumber(len, TLV_LEN);
if (len > 0)
write(b);
}
public void writeSignature(byte[] signature, PublicKey pubKey) throws IOException {
if (!pubKey.getAlgorithm().equals("DSA"))
throw new UnsupportedOperationException();
out.write(signature);
}
public void writeMysteriousX(SignatureX x) throws IOException {
writePublicKey(x.longTermPublicKey);
writeInt(x.dhKeyID);
writeSignature(x.signature, x.longTermPublicKey);
}
public void writeMysteriousX(SignatureM m) throws IOException {
writeBigInt(m.localPubKey.getY());
writeBigInt(m.remotePubKey.getY());
writePublicKey(m.localLongTermPubKey);
writeInt(m.keyPairID);
}
public void writeMysteriousT(MysteriousT t) throws IOException {
writeShort(t.protocolVersion);
writeByte(t.messageType);
writeByte(t.flags);
writeInt(t.senderKeyID);
writeInt(t.recipientKeyID);
writeDHPublicKey(t.nextDH);
writeCtr(t.ctr);
writeData(t.encryptedMessage);
}
}