//////////////////////////////////////////////////////////////////////////// 
// 
// 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.provider;

import java.util.Arrays;
import java.io.ByteArrayOutputStream;
import java.math.BigInteger;
import java.util.Random;
import java.security.SignatureSpi;
import java.security.PublicKey;
import java.security.PrivateKey;
import java.security.MessageDigest;
import java.security.InvalidParameterException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.SignatureException;
import java.security.spec.AlgorithmParameterSpec;
import javax.crypto.Cipher;
import com.dstc.security.asn1.Sequence;
import com.dstc.security.asn1.OctetString;
import com.dstc.security.asn1.Asn1Exception;
import com.dstc.security.x509.AlgorithmId;
import com.dstc.security.x509.SubjectPublicKeyInfo;

/**
 * <p>An abstract class representing a signature algorithm with RSA
 * any any message Digest algorithm.
 *
 * @see java.security.Signature
 * @see com.dstc.security.provider.RSAwithMD2
 * @see com.dstc.security.provider.RSAwithMD4
 * @see com.dstc.security.provider.RSAwithSHA1
 * @see com.dstc.security.provider.RSAwithRIPEMD160
 *
 * @version 0.98, 98/07/01
 * @author Ming Yung
 */
public abstract class RSAwithAnyMD extends SignatureSpi 
{
  protected RSAPublicKey pubKey;
  protected RSAPrivateCrtKey privKey;
  protected byte data[];
  protected MessageDigest md;
  protected String mdOid;
  private DigestInfo digestInfo;
  private ByteArrayOutputStream bos;
  private Cipher rsa;

  protected void engineSetParameter(String param, Object value)
    throws InvalidParameterException
  {
    //Deprecated. Does nothing
  }

  protected Object engineGetParameter(String param)
    throws InvalidParameterException
  {
    //Deprecated.
    return null;
  }

  protected void engineSetParameter(AlgorithmParameterSpec params) 
    throws InvalidAlgorithmParameterException
  {
    //Does nothing
  }

  protected void engineInitVerify(PublicKey publicKey) 
    throws InvalidKeyException 
  {
    try
    {
      rsa = Cipher.getInstance("RSA/1/PKCS1Padding", "DSTC");
      rsa.init(Cipher.DECRYPT_MODE, publicKey);
      bos = new ByteArrayOutputStream();
    }
    catch (InvalidKeyException e)
    {
      throw e;
    }
    catch (Exception e)
    {
      e.printStackTrace();
    }
  }

  protected void engineInitSign(PrivateKey privateKey) 
    throws InvalidKeyException 
  {
    try
    {
      rsa = Cipher.getInstance("RSA/1/PKCS1Padding", "DSTC");
      rsa.init(Cipher.ENCRYPT_MODE, privateKey);
      bos = new ByteArrayOutputStream();
    }
    catch (InvalidKeyException e)
    {
      throw e;
    }
    catch (Exception e)
    {
      e.printStackTrace();
    }
  }

  protected void engineUpdate(byte b) throws SignatureException 
  {
    byte temp[] = {b};
    this.engineUpdate(temp, 0, 1);
  }

  protected void engineUpdate(byte b[], int off, int len) 
    throws SignatureException 
  {
    bos.write(b, off, len);
  }

  ///Returns the bit representation of an integer
  protected byte[] engineSign() throws SignatureException 
  {
    try
    {
      byte toBeEncrypted[];

      byte[] data = bos.toByteArray();
      if (data.length == 0)
        throw new SignatureException("Nothing to sign");

      if (mdOid != null)
        toBeEncrypted = (new DigestInfo(mdOid, data)).encode();
      else
        toBeEncrypted = data;

      return rsa.doFinal(toBeEncrypted);
    }
    catch (Exception e)
    {
      throw new SignatureException(e.toString());
    }
  }

  ///Expects sigBytes to be the bit representation of an integer
  protected boolean engineVerify(byte sigBytes[]) throws SignatureException 
  {
    try
    {
      byte[] orig, computed;

      byte[] data = bos.toByteArray();
      if (data.length == 0)
        throw new SignatureException("Nothing to verify");

      byte decrypted[] = rsa.doFinal(sigBytes);
  
      if (mdOid != null)
      {
        orig = md.digest(data);
        computed = (new DigestInfo(decrypted)).getBytes();
      }
      else
      {
        orig = data;
        computed = decrypted;
      }
  
      if(!Arrays.equals(orig, computed))
        return false;
      else
        return true;
    }
    catch (Exception e)
    {
      throw new SignatureException(e.toString());
    }
  }

  /**
   * <p>Inner class representing ASN.1 structure DigestInfo.
   */
  protected class DigestInfo extends Sequence
  {
    private String oid;
    private byte bytes[];

    protected DigestInfo(String oid, byte bytes[]) throws Asn1Exception
    {
      this.oid = oid;
      this.addComponent(new AlgorithmId(oid, null));
      this.bytes = bytes;
      this.addComponent(new OctetString(md.digest(bytes)));
    }

    protected DigestInfo(byte encoded[]) throws Asn1Exception
    {
      doDecode(encoded);
      this.bytes = ((OctetString)components.elementAt(1)).getBytes();
    }

    protected byte[] getBytes()
    {
      return bytes;
    }
  }
}
