package proof;

import sdsi.*;
import sdsi.sexp.*;
import Tools.*;

import java.security.*;
import java.util.*;

/**
 * This proof proves that a principal speaks for itself; but it
 * considers the fact that the principal has multiple representations
 * (original plus hashes). That is, the hash of a principal is just
 * an unambiguous shorthand notation for the principal itself.
 * In the logic, the hash is formally treated as a separate principal (hence
 * the need for this proof class), but we also assume that
 * <center>{@tex $H_A = A$}</center>
 * That is, 
 * <center>{@tex $(H_A \speaksfor A) \wedge (A \speaksfor H_A)$}</center>
 */
public class HashProof
	extends Proof {

	SDSIPrincipal issuer;
	Subject subject;

	static final String LABEL = "hash-equivalence";

	public HashProof(SexpList list)
		throws SexpParseException {
		if (!(list.getType().equals(Proof.LABEL)
				&& ((SexpString) list.elementAt(1)).stringContent()
					.equals(HashProof.LABEL)
				&& list.size()==4
				&& (list.elementAt(2) instanceof SexpList)
			)) {
			throw new SexpParseException(
				"incorrectly formatted "+HashProof.LABEL+" proof");
		}
		
		subject = (Subject) KeyTools.parseSexp(list.elementAt(2));
		issuer = (SDSIPrincipal) KeyTools.parseSexp(list.elementAt(3));
		if (!KeyTools.arePrincipalsEquivalent((SDSIObject) subject, issuer)) {
			throw new SexpParseException(
				HashProof.LABEL+" proof is false (hash doesn't match)");
		}

		srep = list;
	}

	public HashProof(SDSIPrincipal thePrincipal,
		boolean hashIsSubject, String hashType)
		throws NoSuchAlgorithmException {
		Hash hash = new Hash(hashType, thePrincipal);
		if (hashIsSubject) {
			subject = hash;
			issuer = thePrincipal;
		} else {
			subject = thePrincipal;
			issuer = hash;
		}

		Sexp[] sexpary = new Sexp[4];
		sexpary[0] = new SexpString("proof");
		sexpary[1] = new SexpString(HashProof.LABEL);
		sexpary[2] = subject.getSrep();
		sexpary[3] = issuer.getSrep();
		srep = new SexpList(sexpary);
	}

	public SDSIPrincipal getIssuer() {
		return issuer;
	}

	public Subject getSubject() {
		return subject;
	}

	/**
	 * verify that the proof steps are indeed valid, and that they
	 * combine as advertised to show the claimed result
	 */
	protected void directVerify()
		throws InvalidProofException {
		if (!KeyTools.arePrincipalsEquivalent((SDSIObject) subject, issuer)) {
			throw new InvalidProofException("Hash does not check out");
		}
	}

	void preorder(List addToMe, int what) {
		// if this node had its own issuer, we'd insert it here.
		switch (what) {
			case Proof.ISSUERS:
				addToMe.add(getIssuer());
				break;
			case Proof.CERTIFICATES:
				break;
			case Proof.PROOFS:
				addToMe.add(this);
				break;
			default:
				throw new RuntimeException("undefined what.");
		}
	}

	public Tag getTag() {
		return Tag.getTagStar();
	}
}
