// DSigDemo.java
// (c) COPYRIGHT MIT and INRIA, 1997.
// Please first read the full copyright statement in file COPYRIGHT.html

// The source code is jointly developed by W3C and some of its members
// participating in the Digital Signature Initiative.  The authors are:
//    Mark Champine, HP, <champine@apollo.hp.com>
//    Yang-hua Chu, MIT/W3C, <yhchu@w3.org>
//    Vasanthan Dasan, Sun, <vasanthan.dasan@central.sun.com>
//    Peter Lipp, University of Technology, Graz <plipp@iaik.tu-graz.ac.at>
//    Andreas Sterbenz, U. of Technology, Graz <sterbenz@iaik.tu-graz.ac.at>

package w3c.pics.parser;

import java.io.*;
import java.security.*;
import java.security.interfaces.*;
import java.math.BigInteger;
import java.util.*;

import w3c.www.dsig.*;
import w3c.crypto.us.dsig.*;

/**
 * DSig Demo class.
 * <P>
 * This class demonstrates the use of DSig library (w3c.www.dsig package)
 * with the PICS parser library (w3c.pics.parser package).
 *
 * @version 1.0beta (last modified 08-February-1998)
 * @author Andreas Sterbenz
 * @author Yang-hua Chu
 */

public class DSigDemo {
 
 private static String labelfname = "k:\\projects\\dsig\\w3c\\src\\label1.lbl";
 private static LabelParser lparser;

  private static final boolean DEBUG = false;

  private static void println(String text)
  {
    System.out.println(text);
  }

  private static void usage()
  {
    println("Usage: java DSigDemo [LabelFile]");
    println("If LabelFile is not specified, it defaults to '" + 
	    labelfname + "'.");
    waitKey();
    System.exit(1);
  }

  private static KeyPair getDssKeyPair(boolean generateKeys)
  {
    BigInteger x = new BigInteger("427680156495144453885115690373649756045959408862");
    BigInteger y = new BigInteger("177339384197349739065818268761102971597420546697560629529304621486431387773923117551613374450730394130117771062930596087436245169541121954274843478181992055685752245183950336677657126495585057247936207232282564918317794169791277754707537125797798694653557421342631893997403826497768230679355141661766921531808");
    BigInteger p = new BigInteger("178011905478542266528237562450159990145232156369120674273274450314442865788737020770612695252123463079567156784778466449970650770920727857050009668388144034129745221171818506047231150039301079959358067395348717066319802262019714966524135060945913707594956514672855690606794135837542707371727429551343320695239");
    BigInteger q = new BigInteger("864205495604807476120572616017955259175325408501");
    BigInteger g = new BigInteger("174068207532402095185811980123523436538604490794561350978495831040599953488455823147851597408940950725307797094915759492368300574252438761037084473467180148876118103083043754985190983472601550494691329488083395492313850000361646482644608492304078721818959999056496097769368017749273708962006689187956744210730");
    PublicKey pubKey = new DSAPublicKeyClass(y, p, q, g);
    PrivateKey privKey = new DSAPrivateKeyClass(x, p, q, g);
    return new KeyPair(pubKey, privKey);
  }

  private static KeyPair getRSAKeyPair(boolean generateKeys)
  {
    boolean printKeys = false;
    KeyPair keypair;
    if( generateKeys ) {
      System.out.println("\n*** Generating an RSA key pair...");
      KeyPairGenerator keyGen;
      try {
        keyGen = KeyPairGenerator.getInstance("RSA");
      }
      catch (NoSuchAlgorithmException e) {
        System.out.println("*** Failed to obtain Keypair for RSA");
        System.exit(0);
        return null;
      }
      keyGen.initialize(1024, new SecureRandom());
      keypair = keyGen.generateKeyPair();
    } else {
      BigInteger d = new BigInteger("68138255720869520157647836932754400039691365261764559267550893659906061756871169175861336887764404603987589312426353588286897017208976854927923365667110457304974460011917527923183878645717541442008548233599144081808299559426020066490510197385677987789259492120401582458211101908546011780270481507279433629099");
      BigInteger e = new BigInteger("3");
      BigInteger n = new BigInteger("102207383581304280236471755399131600059537047892646838901326340489859092635306753763792005331646606905981383968639530382430345525813465282391885048500675441046154256003381189770318196651001073900385470516170799242099841653261762687074619320636738305250819319534954674941158413110668521793278983605395044300553");
      if (DEBUG) System.out.println("bitlength of modulus: " + n.bitLength());
      RSAPublicKey pubkey = new RSAPublicKeyImpl(e, n);
      RSAPrivateKey privkey = new RSAPrivateKeyImpl(d, e, n);
      keypair = new KeyPair(pubkey, privkey);
    }
 
    if( printKeys ) {
      RSAPublicKey pubkey = (RSAPublicKey)keypair.getPublic();
      RSAPrivateKey privkey = (RSAPrivateKey)keypair.getPrivate();
      BigInteger d = privkey.getExponent();
      BigInteger e = pubkey.getExponent();
      BigInteger n = pubkey.getModulus();
      System.out.println("d = " + d);
      System.out.println("e = " + e);
      System.out.println("n = " + n);
      waitKey();
      System.exit(0);
    }
    return keypair;
  }

  private static boolean verifyDemo(String labelString) throws Throwable
  {
    println("PART II: Verifying a label");

    println("Step 1: Preparation step - Creating a SigLabel object");
    println("Trying to parse the signed label...");

    // yc: SigLabel label1 = new SigLabel(labelString);
    InputStream ins = new StringBufferInputStream(labelString);
    lparser.ReInit(ins);
    Vector labels = new Vector();
    lparser.labellist(labels);
    Label label1 = (Label) labels.elementAt(0);

    println("Label successfully parsed.");

    // yc: println("The label contents are:\n" + label1.prettyPrint());
    println("The label contents are:\n" + label1.toString());

    waitKey();
    
    label1.initDSig();
    
    println("Quick signature verification...");

    println("\nThe excluded label looks like:");
    println(label1.toExcludedDSigString(new Hashtable())+"\n");

    waitKey();

//     Trivalue res;
//     println("Quick signature verification...");
//     label1.initDSig();
//     res = label1.verify();
//     println("Verification result: " + res.toString());

    Trivalue result = Trivalue.newTrue();

    for (Enumeration e=label1.getResInfo().digests(); e.hasMoreElements(); ) {
      String alg = (String)e.nextElement();
      Trivalue res = label1.getResInfo().verify(alg, label1);
      System.out.println("ResInfo : " + alg + " : " + res.toString());
      result = result.and(res);
    }
    
    for (Enumeration e=label1.getSigBlock().sigSuites();e.hasMoreElements();) {
      SignatureSuite suite = (SignatureSuite)e.nextElement();
      Trivalue res = suite.verify(label1, null);
      System.out.println("SigBlock: " + suite.getURL()+" : "+res.toString());
      result = result.and(res);
    }
    println("Verification result: " + result.toString());
    waitKey();
    return true;
  }

  private static String signDemo(String filename, boolean isUS, boolean isECC) 
       throws Throwable
  {
    println("PART I: Signing a label");

    println("Step 1: Preparation step - Creating a SigLabel object");
    println("Trying to load and parse label from file '" + filename + "'...");

    //yc: SigLabel label1 = new SigLabel(new FileInputStream(filename));
    lparser.ReInit(new FileInputStream(filename));
    // LabelParser lparser = new LabelParser(new FileInputStream(filename));
    Vector labels = new Vector();
    lparser.labellist(labels);
    Label label1 = (Label) labels.elementAt(0);
    
    println("Label successfully parsed.");

    // yc: println("The label contents are:\n" + label1.prettyPrint());
    println("The label contents are:\n" + label1.toString());

    waitKey();

    println("Step 2: Build the ResInfo - add hashes to the label");

    // yc: addition
    label1.initDSig();   
    
    ResInfoExtension resinfo = label1.getResInfo();
    resinfo.addDigest(HashRegistry.MD5URL, label1, null);
    resinfo.addDigest(HashRegistry.SHA1URL, label1,null);
    
    // yc: println("The modified label contents are:\n"+label1.prettyPrint());
    label1.updateDSig();
    println("The modified label contents are:\n"+label1.toString());

    waitKey();
    println("Step 3: Add digital signatures to the SigBlock");
 
    if (isECC) {
      eccDsig tec = new eccDsig();   
      tec.ecc_init();                 //set up ecc parameters
      EccKeyPair eccpair = tec.ecc_genKeyPair();
      // KeyPair pair = new KeyPair(eccpair.getPublic(), eccpair.getPrivate());
      // addSignature("http://www.w3.org/PICS/DSig/ECDSA-SHA1-1_0.html",
      //		   "ECC/DSA/SHA-1", pair, label1);
      println("Add ECC with DSS signature to the SigBlock");
      
      SignatureSuite suite = SigSuiteRegistry.getInstance("http://www.w3.org/PICS/DSig/ECDSS-1_0.html");

      println("\nThe excluded label looks like:");
      println(label1.toExcludedDSigString(suite.getSuiteValues())+"\n");

      // yc-m: sigdss.sign(label1, privkey);
      // yc-m:println("The modified label contents are:\n"+label1.prettyPrint());
      label1.sign(suite, eccpair.getPrivate());
      // yc-comment: setByKey disappear if used before sign
      suite.setBy("ByKey", eccpair.getPublic());
      label1.updateDSig();

      println("The modified label contents are:\n"+label1.toString());
      waitKey();
    }      

    // yc-m: make the process into a separate method
    addSignature(SigSuiteRegistry.DSSURL, "DSS",
		 getDssKeyPair(false), label1);
    
    if (isUS) {
      addSignature(SigSuiteRegistry.RSAMD5URL, "RSA/MD5",
		   getRSAKeyPair(false), label1);
      addSignature(SigSuiteRegistry.RSASHA1URL, "RSA/SHA1",
		   getRSAKeyPair(false), label1);
    }

    return label1.toString();
  }

  private static void addSignature(String suiteURL, String shortname,
				  KeyPair keypair, Label label1) 
       throws Throwable
  {
    println("Add " + shortname + " signature to the SigBlock");

    
    SignatureSuite suite = SigSuiteRegistry.getInstance(suiteURL);
    PrivateKey privkey = keypair.getPrivate();

    println("\nThe excluded label looks like:");
    println(label1.toExcludedDSigString(suite.getSuiteValues())+"\n");

    // yc-m: sigdss.sign(label1, privkey);
    // yc-m:println("The modified label contents are:\n"+label1.prettyPrint());
    label1.sign(suite, privkey);
    // yc-comment: setByKey disappear if used before sign
    suite.setBy("ByKey", keypair.getPublic());
    label1.updateDSig();

    println("The modified label contents are:\n"+label1.toString());
    waitKey();
  }    

  public static void main(String args[])
  {
    try {
      lparser = new LabelParser(new StringBufferInputStream(" "));
      Trivalue res;

      println("This is a simple demo application for the W3C DSig Reference Implementation.");
      println("It will demonstrate how to create and verify DSig signature labels.\n");
      switch( args.length ) {

        case 0:
          println("The label will be loaded from '" + labelfname + "',\nuse a commandline argument to specify a different file.");
          break;

        case 1:
          labelfname = args[0];
	  
          println("The label will be loaded from '" + labelfname + "'.");
          break;

        default:
          usage();
          break;
      }

      waitKey();
      
      boolean isUS = false;
      String tmp = DSigUtil.readln("US version?", "n");
      if (tmp.equals("y") || tmp.equals("Y")) isUS = true;
      
      boolean isECC = false;
      if (isUS) {
	tmp = DSigUtil.readln("Run ECC (WinNT required)?", "n");
	if (tmp.equals("y") || tmp.equals("Y")) isECC = true;
      }

      if (isECC) {
	println("Setup: Load ECC DLL");
	// install the ECDSA algorithm in the registry
	//create a signature object (which also loads the dll)
	SigSuiteRegistry.addAlgorithm(SigSuiteECDSS.ECDSSURL, 
				      new SigSuiteECDSS().getClass());
	println("Load was successful.");
      }

      String signedlabel = signDemo(labelfname, isUS, isECC);
      
      verifyDemo(signedlabel);

    } catch( Throwable e ) {
      println("\nAn exception occured: " + e.getMessage());
      e.printStackTrace();
      waitKey();
    }
  }

  static void waitKey()
  {
    println("\nHit the <RETURN> key to continue.");
    try {
      do {
        System.in.read();
      } while( System.in.available() > 0 );
    } catch( java.io.IOException e ) {
      ;
    }
  }
}
