////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) DSTC Pty Ltd (ACN 052 372 577) 1993, 1994, 1995.
// Unpublished work.  All Rights Reserved.
//
// The software contained on this media is the property of the
// DSTC Pty Ltd.  Use of this software is strictly in accordance
// with the license agreement in the accompanying LICENSE.DOC
// file. If your distribution of this software does not contain
// a LICENSE.DOC file then you have no rights to use this
// software in any manner and should contact DSTC at the address
// below to determine an appropriate licensing arrangement.
//
//      DSTC Pty Ltd
//      Level 7, GP South
//      University of Queensland
//      St Lucia, 4072
//      Australia
//      Tel: +61 7 3365 4310
//      Fax: +61 7 3365 4311
//      Email: jcsi@dstc.qut.edu.au
//
// This software is being provided "AS IS" without warranty of
// any kind.  In no event shall DSTC Pty Ltd be liable for
// damage of any kind arising out of or in connection with
// the use or performance of this software.
//
////////////////////////////////////////////////////////////////////////////

package com.dstc.security.kerberos;

import java.util.Date;
import java.security.SecureRandom;

import com.dstc.security.kerberos.crypto.KerberosCipher;
import com.dstc.security.kerberos.crypto.KeyMaterial;
import com.dstc.security.kerberos.crypto.KerberosKey;
import com.dstc.security.kerberos.crypto.CryptoException;
import com.dstc.security.kerberos.creds.Credential;
import com.dstc.security.kerberos.creds.KerberosTicket;
import com.dstc.security.kerberos.v5.base.*;
import com.dstc.security.kerberos.v5.creds.*;
import com.dstc.security.kerberos.v5.crypto.*;
import com.dstc.security.kerberos.v5.crypto.EncryptedData;
import com.dstc.security.kerberos.v5.APReq;
import com.dstc.security.kerberos.v5.BadMessageTypeException;
import com.dstc.security.kerberos.v5.BadVersionException;

import com.dstc.security.asn1.Asn1Exception;

/**
 * Representation of an application request message, mainly used by
 * application clients and servers.
 *
 * @author Ming Yung
 */
public class ApplicationRequest extends KerberosMessage
{
  private String realm = null;
  private int checksumType; 
  private byte[] checksumBytes = null;
  private int cusec = 0;
  private int seqNum = -1;
  private KerberosTime ctime = null;
  private APReq apReq = null;
  private KerberosCipher cipher = null;
  private EncryptedData encData = null;
  private Authenticator auth = null;
  private Ticket ticket = null;
  private PrincipalName clientName = null;
  private boolean useSessionKey = false;
  private boolean mutualAuthRequired = false;

  public ApplicationRequest(Credential serviceCred, SecureRandom rand) 
    throws CryptoException
  {
    this.cipher = new KerberosCipher(serviceCred, rand, null);
    this.cipher.initEncrypt();

    setTicket(serviceCred.getTicket());
    setClientName(serviceCred.getClientName());
  }

  public ApplicationRequest(byte[] encoded) 
    throws Asn1Exception, BadMessageTypeException, BadVersionException
  {
    this.apReq = new APReq(encoded);
    this.encData = apReq.getEncrypted();
    this.ticket = apReq.getTicket();
    this.useSessionKey = apReq.getAPOptions().isUsingSessionKey();
    this.mutualAuthRequired = apReq.getAPOptions().isMutualAuthRequired();
  }

  public void initDecrypt(KeyMaterial cred) throws CryptoException
  {
    KerberosCipher cipher = new KerberosCipher(cred, null, null);
    cipher.initDecrypt();

    this.cipher = cipher;
  }

  public void encrypt()
    throws KerberosException, CryptoException, Asn1Exception,
           BadMessageTypeException
  {
    if (this.cipher == null)
      throw new KerberosException("Cipher not yet set");

    this.auth = new Authenticator(new Realm(realm), clientName, 
                  new Checksum(checksumType, checksumBytes), 
                    cusec, ctime, null, seqNum, null);

    this.encData = cipher.encrypt(auth.encode());

    this.apReq = new APReq(new APOptions(useSessionKey, mutualAuthRequired), 
                   this.ticket, encData);
  }

  public void setRealm(String realm)
  {
    this.realm = realm;
  }

  public void setCusec(int cusec)
  {
    this.cusec = cusec;
  }

  public void setSequenceNumber(int seqNum)
  {
    this.seqNum = seqNum;
  }

  public void setChecksum(byte[] checksum)
  {
    this.checksumBytes = checksum;
  }

  public void setChecksumType(int type)
  {
    this.checksumType = type;
  }

  public void setClientTime(Date ctime)
  {
    this.ctime = new KerberosTime(ctime);
  }

  public void setClientName(byte[] encodedName)
  {
    try
    {
      this.clientName = new PrincipalName(encodedName);
    }
    catch (Asn1Exception e)
    {
      e.printStackTrace();
    }
  }

  public void setUseSessionKey(boolean truth)
  {
    this.useSessionKey = truth;
  }
  
  public void setMutualAuthRequired(boolean truth)
  {
    this.mutualAuthRequired = truth;
  }
  
  public void decrypt() 
    throws KerberosException, CryptoException, Asn1Exception,
           BadMessageTypeException
  {
    if (this.cipher == null)
      throw new KerberosException("Cipher not yet set");

    this.auth
      = new Authenticator(this.cipher.decrypt(encData));
  }

  public void setTicket(KerberosTicket ticket)
  {
    try
    {
      this.ticket = new Ticket(ticket.getEncoded());
    }
    catch (Exception e)
    {
      e.printStackTrace();
    }
  }

  public KerberosTicket getTicket()
  {
    return new KerberosTicket(this.ticket);
  }

  public String getClientName()
  {
    return this.auth.getClientName().toString();
  }

  public byte[] getEncodedClientName()
  {
    return this.auth.getClientName().encode();
  }

  public int getSequenceNumber()
  {
    return this.auth.getSeqNumber();
  }

  public Date getClientTime()
  {
    return this.auth.getCTime().getDate();
  }

  public byte[] getChecksumBytes()
  {
    return this.auth.getChecksum().getBytes();
  }

  public int getCusec()
  {
    return this.auth.getCusec();
  }

  public boolean isUsingSessionKey()
  {
    return this.useSessionKey;
  }

  public boolean isMutualAuthRequired()
  {
    return this.mutualAuthRequired;
  }

  public byte[] getSubKeyBytes()
  {
    EncryptionKey subKey;
    if (this.auth != null)
      subKey = this.auth.getSubKey();
    else
      return null;

    if (subKey != null)
      return subKey.getKeyBytes();
    else
      return null;
  }

  public byte[] getEncoded()
  {
    return this.apReq.encode();
  }
}
