//////////////////////////////////////////////////////////////////////////// 
// 
// 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.v5.creds;

import java.util.Vector;
import java.util.Enumeration;

import com.dstc.security.kerberos.v5.BadVersionException;
import com.dstc.security.kerberos.v5.BadMessageTypeException;
import com.dstc.security.kerberos.v5.base.*;
import com.dstc.security.kerberos.v5.crypto.Checksum;
import com.dstc.security.kerberos.v5.crypto.EncryptionKey;

import com.dstc.security.asn1.Asn1Exception;
import com.dstc.security.asn1.Asn1;
import com.dstc.security.asn1.Explicit;
import com.dstc.security.asn1.Sequence;

/**
 * <p>A class representing the Kerberos ASN.1 structure Authenticator.
 *
 * <pre>
 * Authenticator :== [APPLICATION 2] SEQUENCE {
 *                    authenticator-vno[0]    INTEGER,
 *                    crealm[1]               Realm,
 *                    cname[2]                PrincipalName,
 *                    cksum[3]                Checksum OPTIONAL,
 *                    cusec[4]                INTEGER,
 *                    ctime[5]                KerberosTime,
 *                    subkey[6]               EncryptionKey OPTIONAL,
 *                    seq-number[7]           INTEGER OPTIONAL,
 *                    auth-data[8]            AuthorizationData OPTIONAL
 * }
 * </pre>
 *
 * @version 0.98, 98/07/01
 * @author Ming Yung
 */
public final class Authenticator extends Explicit
{
  public static final int TAG_KRB_AUTHENTICATOR = 2;
  private static final int pvno = 5;
  private Realm crealm;
  private PrincipalName cName;
  private Checksum cksum;
  private int cusec;
  private KerberosTime ctime;
  private EncryptionKey subkey;
  private int seqNum = -1;
  private AuthorizationData authData;

  /**
   * Constructs an Authenticator from individual components.
   *
   * @param realmThe client's realm.
   * @param cNameThe client name.
   * @param cksumThe optional Checksum, could be null.
   * @param cusecThe microsecond part of the client's timestamp.
   * @param ctimeThe client's timestamp.
   * @param encKeyThe optional sub-session key, could be null.
   * @param seqNumThe optional sequence number to be used, negative values
   *                    indicate that sequence number not to be included.
   * @param authDataThe optional authorization data, could be null.
   */
  public Authenticator(Realm realm, PrincipalName cName, Checksum cksum, 
                       int cusec, KerberosTime ctime, EncryptionKey encKey,
                       int seqNum, AuthorizationData authData)
  {
    super(Asn1.TAG_CLASS_APPLICATION, TAG_KRB_AUTHENTICATOR);

    Sequence seq = new Sequence();

    Explicit exp = new Explicit(Asn1.TAG_CLASS_CONTEXT, 0);
    exp.addComponent(new com.dstc.security.asn1.Integer(pvno));
    seq.addComponent(exp);

    this.crealm = realm;
    exp = new Explicit(Asn1.TAG_CLASS_CONTEXT, 1);
    exp.addComponent(realm);
    seq.addComponent(exp);

    this.cName = cName;
    exp = new Explicit(Asn1.TAG_CLASS_CONTEXT, 2);
    exp.addComponent(cName);
    seq.addComponent(exp);

    if (cksum !=null)
    {
      this.cksum = cksum;
      exp = new Explicit(Asn1.TAG_CLASS_CONTEXT, 3);
      exp.addComponent(cksum);
      seq.addComponent(exp);
    }

    this.cusec = cusec;
    exp = new Explicit(Asn1.TAG_CLASS_CONTEXT, 4);
    exp.addComponent(new com.dstc.security.asn1.Integer(cusec));
    seq.addComponent(exp);

    this.ctime = ctime;
    exp = new Explicit(Asn1.TAG_CLASS_CONTEXT, 5);
    exp.addComponent(ctime);
    seq.addComponent(exp);

    if (encKey != null)
    {
      this.subkey = encKey;
      exp = new Explicit(Asn1.TAG_CLASS_CONTEXT, 6);
      exp.addComponent(encKey);
      seq.addComponent(exp);
    }

    if (seqNum >= 0)
    {
      this.seqNum = seqNum;
      exp = new Explicit(Asn1.TAG_CLASS_CONTEXT, 7);
      exp.addComponent(new com.dstc.security.asn1.Integer(seqNum));
      seq.addComponent(exp);
    }

    if (authData != null) {
      this.authData = authData;
      exp = new Explicit(Asn1.TAG_CLASS_CONTEXT, 8);
      exp.addComponent(authData);
      seq.addComponent(exp);
    }

    addComponent(seq);
  }

  /**
   * Constructs an Authenticator from a DER encoding
   */
  public Authenticator(byte encoded[]) throws Asn1Exception, BadVersionException
  {
    doDecode(encoded);

    if (this.getTagNumber() != TAG_KRB_AUTHENTICATOR)
      throw new Asn1Exception("Bad Tag Number");

    Sequence seq = (Sequence)this.getComponent();
    Enumeration comps = seq.getComponents().elements();

    Explicit exp = (Explicit)comps.nextElement();
    if (exp.getTagNumber() != 0)
      throw new Asn1Exception("Bad Tag Number");
    if (((com.dstc.security.asn1.Integer)exp.getComponent()).getInt() != pvno)
      throw new BadVersionException("Not version 5");

    exp = (Explicit)comps.nextElement();
    if (exp.getTagNumber() != 1)
      throw new Asn1Exception("Bad Tag number");
    this.crealm = new Realm(exp.getComponent().encode());

    exp = (Explicit)comps.nextElement();
    if (exp.getTagNumber() != 2)
      throw new Asn1Exception("Bad Tag number");
    this.cName = new PrincipalName(exp.getComponent().encode());

    exp = (Explicit)comps.nextElement();
    if (exp.getTagNumber() == 3) {
      this.cksum = new Checksum(exp.getComponent().encode());
      exp = (Explicit)comps.nextElement();
    }

    if (exp.getTagNumber() != 4)
      throw new Asn1Exception("Bad Tag number");
    this.cusec = ((com.dstc.security.asn1.Integer)exp.getComponent()).getInt();

    exp = (Explicit)comps.nextElement();
    if (exp.getTagNumber() != 5)
      throw new Asn1Exception("Bad Tag number");
    this.ctime = new KerberosTime(exp.getComponent().encode());

    int prevTag = 5, tag;
    while (comps.hasMoreElements()) {
      exp = (Explicit)comps.nextElement();
      tag = exp.getTagNumber();
      if (tag <= prevTag) throw new Asn1Exception("Sequence out of order");
      prevTag = tag;

      switch (tag) {
      case 6:    
        this.subkey = new EncryptionKey(exp.getComponent().encode());
        break;
      case 7:
        this.seqNum 
          = ((com.dstc.security.asn1.Integer)exp.getComponent()).getInt();
        break; 
      case 8:
this.authData = new AuthorizationData(exp.getComponent().encode());
break;
      default:
throw new Asn1Exception("Invalid context tag number in sequence");
      }
    }
  }

  /**
   * Returns the (client) Realm for this Authenticator
   */
  public Realm getRealm() 
  {
    return this.crealm;
  }

  /**
   * Returns the client name for this Authenticator
   */
  public PrincipalName getClientName() 
  {
    return this.cName;
  }

  /**
   * Returns the checksum for this Authenticator, could be null
   */
  public Checksum getChecksum() 
  {
    return this.cksum;
  }

  /**
   * Returns the microsecond part of the timestamp for this Authenticator
   */
  public int getCusec() 
  {
    return this.cusec;
  }

  /**
   * Returns the timestamp for this Authenticator
   */
  public KerberosTime getCTime() 
  {
    return this.ctime;
  }

  /**
   * Returns the sub-session key for this Authenticator, could be null
   */
  public EncryptionKey getSubKey() 
  {
    return this.subkey;
  }

  /**
   * Returns the sequence number in this Authenticator. A negative value means
   * that no sequence number is specified in this Authenticator.
   */
  public int getSeqNumber() 
  {
    return this.seqNum;
  }

  /**
   * Returns the authorization data for this Authenticator, could be null
   */
  public AuthorizationData getAuthData() 
  {
    return this.authData;
  }
}
