////////////////////////////////////////////////////////////////////////////
//
// 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.CryptoException;
import com.dstc.security.kerberos.v5.base.*;
import com.dstc.security.kerberos.v5.crypto.EncryptedData;
import com.dstc.security.kerberos.v5.APRep;
import com.dstc.security.kerberos.v5.EncAPRepPart;
import com.dstc.security.kerberos.v5.BadMessageTypeException;
import com.dstc.security.kerberos.v5.BadVersionException;

import com.dstc.security.asn1.Asn1Exception;

/**
 * Representation of an application response message, mainly used by
 * application clients and servers.
 *
 * @author Ming Yung
 */
public class ApplicationResponse extends KerberosMessage
{
  private APRep rep = null;
  private KerberosCipher cipher = null;
  private EncAPRepPart encAPRepPart = null;
  private EncryptedData encData = null;
  private KerberosTime clientTime = null;
  private int cusec;
  private int seqNum;
  private byte[] key = null;

  public ApplicationResponse(KeyMaterial km, SecureRandom rand) 
    throws CryptoException
  {
    this.cipher = new KerberosCipher(km, rand, null);
    this.cipher.initEncrypt();
  }

  /**
   * Constructs an ApplicationResponse from a DER encoding of an APRep
   */
  public ApplicationResponse(byte[] encoded) 
    throws Asn1Exception, BadVersionException, BadMessageTypeException
  {
    this.rep = new APRep(encoded);
    this.encData = rep.getEncrypted();
  }

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

    this.cipher = cipher;
  }

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

    this.encAPRepPart 
      = new EncAPRepPart(this.cipher.decrypt(encData));
  }

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

    this.encAPRepPart = new EncAPRepPart(clientTime, cusec, null, seqNum);

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

    this.rep = new APRep(encData);
  }

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

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

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

  public int getSequenceNumber()
  {
    return this.encAPRepPart.getSequenceNumber();
  }

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

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

  public byte[] getSubSessionKey()
  {
    return this.encAPRepPart.getEncryptionKey().getKeyBytes();
  }

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