//SecureUDPClient.java

import java.io.*;
import java.net.*;
import krb5.lib.*;

class SecureUDPClient {
	public static void main(String args[]) throws UnknownHostException {
		do {
			subMain(args);
		} while(true);
	}

	public static void subMain(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 = 8889;

		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);

			String str = " 01234567ABCDEFGHello; this is a secure message!";
			byte[] userData = new byte[str.length()];
			str.getBytes(0, str.length(), 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
			);

			System.out.println("send ap_req");

			client.send(ap_req.msg());
			byte[] ibuf = client.receive();

			System.out.println("received ap_rep");
			
			KrbApRep ap_rep = new KrbApRep(
				ibuf,
				tgs_rep.creds()
			);
			ap_rep.authenticate(ap_req);

			System.out.println("authenticated");
			System.out.println("server=\"" + tgs_rep.creds().server + "\"");
			System.out.println();

			do {

				System.out.println("userData=\"" + new String(userData, 0) + "\"");

				KrbMkPriv krb_priv = new KrbMkPriv(
					userData,
					tgs_rep.creds(),
					null, //EncryptionKey subSessionKey
					new KerberosTime(KerberosTime.NOW),
					seqNumber,
					new HostAddress(),
					new HostAddress(InetAddress.getLocalHost())
				);

				System.out.println("send");

				client.send(krb_priv.msg());
				ibuf = client.receive();

				System.out.println("received");
				
				KrbRdPriv krb_priv_reply = new KrbRdPriv(
					ibuf,
					tgs_rep.creds(),
					null, //EncryptionKey subSessionKey
					seqNumber,
					new HostAddress(client.getInetAddress()),
					new HostAddress(),
					true, //boolean timestampRequired
					true //boolean seqNumberRequired
				);

				System.out.println("decrypted");

				byte[] replyData = krb_priv_reply.data();

				System.out.println("replyData=\"" + new String(replyData, 0) + "\"");
				System.out.println();

				//try {
				//	Thread.currentThread().sleep(1000);
				//} catch (InterruptedException e) { }

			} while (false);

		} catch (KrbException e) {
			e.printStackTrace();
			System.out.println(e.krbErrorMessage());
		} catch (Exception e) {
			e.printStackTrace();
		}

		System.out.println("ok");
	}
}