//SafeUDPClient.java

import java.io.*;
import java.net.*;
import krb5.lib.*;
import krb5.lib.asn1.*;

class SafeUDPClient {
	public static void main(String args[]) throws UnknownHostException {
		String principal = "sanfilip@drdoom.osf.org";
		StringBuffer password = new StringBuffer("somewha0");
		String kdc = "drdoom.osf.org";
		String service = "khttp/drdoom.osf.org@drdoom.osf.org";
		InetAddress address = InetAddress.getLocalHost();
		int port = 8888;

		try {

			//as_req

			KrbAsReq as_req = new KrbAsReq(
				password, //for pre-authentication
				new KDCOptions(),
				new PrincipalName(principal),
				null, //PrincipalName sname
				null, //KerberosTime from
				null, //KerberosTime till
				null, //KerberosTime rtime
				null, //int[] eTypes
				null, //HostAddresses addresses
				null //Ticket[] additionalTickets
			);

			System.out.println("send as_req");

			as_req.send(kdc);
			KrbAsRep as_rep = as_req.getKrbAsRep(password);

			System.out.println("received as_rep");

			//tgs_req

			KrbTgsReq tgs_req = new KrbTgsReq(
				new KDCOptions(),
				as_rep.creds(),
				new ServiceName(service),
				null, //KerberosTime from
				null, //KerberosTime till
				null, //KerberosTime rtime
				null, //int[] eTypes
				null, //HostAddresses addresses
				null, //AuthorizationData authorizationData
				null, //Ticket[] additionalTickets
				null //EncryptionKey subSessionKey
			);

			System.out.println("send tgs_req");

			tgs_req.send(kdc);
			KrbTgsRep tgs_rep = tgs_req.getKrbTgsRep(as_rep.creds());

			System.out.println("received tgs_rep");
			System.out.println();

			UDPClient client = new UDPClient(address, port);

			byte[] userData = util.string2byte(
				" 01234567ABCDEFGHello; this is a safe message!");

			do {

				System.out.println("userData=\"" + new String(userData, 0) + "\"");

				LocalSeqNumber seqNumber = new LocalSeqNumber();

				KrbApReq ap_req = new KrbApReq(
					new APOptions(Krb5.AP_OPTS_MUTUAL_REQUIRED),
					tgs_rep.creds(),
					null, //Checksum cksum
					null, //EncryptionKey subSessionKey
					seqNumber,
					null  //AuthorizationData authzData
				);

				KrbMkSafe krb_safe = new KrbMkSafe(
					userData,
					tgs_rep.creds(),
					null, //EncryptionKey subSessionKey
					new KerberosTime(KerberosTime.NOW),
					seqNumber,
					new HostAddress(),
					new HostAddress(InetAddress.getLocalHost())
				);

				ByteArrayOutputStream os = new ByteArrayOutputStream(); 
				os.write(ap_req.msg());
				os.write(krb_safe.msg());
				byte[] obuf = os.toByteArray();

				System.out.println("send");

				client.send(obuf);
				byte[] ibuf = client.receive();

				EncodeRef ref = new EncodeRef(ibuf);
				int part1_length = ref.end;
				byte[] part1 = new byte[part1_length];
				System.arraycopy(ibuf, 0, part1, 0, part1_length);

				System.out.println("received");
				
				KrbApRep ap_rep = new KrbApRep(
					part1,
					tgs_rep.creds()
				);
				ap_rep.authenticate(ap_req);

				System.out.println("authenticated");
				System.out.println("server=\"" + tgs_rep.creds().server + "\"");
				System.out.println();

				ref.next();
				int part2_length = ref.end - part1_length;
				byte[] part2 = new byte[part2_length];
				System.arraycopy(ibuf, part1_length, part2, 0, part2_length);

				KrbRdSafe krb_safe_reply = new KrbRdSafe(
					part2,
					tgs_rep.creds(),
					null, //EncryptionKey subSessionKey
					seqNumber,
					new HostAddress(client.getInetAddress()),
					new HostAddress(),
					true, //timestampRequired
					false //seqNumberRequired
				);

				System.out.println("verified");

				byte[] replyData = krb_safe_reply.data();

				System.out.println("replyData=\"" + new String(replyData, 0) + "\"");
				System.out.println();

				try {
					Thread.currentThread().sleep(1000);
				}
				catch (InterruptedException e) { }

			}
			while (true);

		}
		catch (KrbException e) {
			e.printStackTrace();
			System.out.println(e.krbErrorMessage());
		}
		catch (Exception e) {
			e.printStackTrace();
		}

		System.out.println("ok");
	}
}