package proof;

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

import java.util.*;

/**
 * This proof verifies a ``self-evident statement'' of the public key
 * signature variety. That is, it verifies
 * <center>{@tex $A\ \says\ B \sfrg{T} A$}</center>
 * when <em>A</em> is a public key and we have <em>A</em>'s signature
 * on an S-expression that says {@tex $B \sfrg{T} A$}.
 *
 * @todo This proof includes belief in an application of the handoff rule.
 * This is one place where we'd decide how often we believed in it.
 */
public class SignedCertificateProof
	extends Proof {

	/**
	 * The signed certificate itself, include the signed auth.
	 */
	SignedCertificate sc;

	/**
	 * The cert inside sc.
	 */
	Cert cert;

	/**
	 * proof that certificate signer speaks for issuer. Null if it's
	 * obvious (they're equivalent up to hash).
	 */
	Proof handoff;

	static final String LABEL = "signed-certificate";

	public SignedCertificateProof(SexpList list)
		throws SexpParseException {
		Assert.assert(list.getType().equals(Proof.LABEL)
					&& ((SexpString) list.elementAt(1)).stringContent()
						.equals(SignedCertificateProof.LABEL));
		if (list.size()>4) {
			throw new SexpParseException("Signed Certificate proof malformed");
		}
		sc = new SignedCertificate((SexpList) list.elementAt(2));
		this.cert = sc.getCertificate();
		if (list.size()==4) {
			this.handoff = Proof.parse((SexpList) list.elementAt(3));
		}
		srep = list;
	}

	public SignedCertificateProof(SignedCertificate sc) {
		this(sc, null);
	}

	public SignedCertificateProof(SignedCertificate sc, Proof handoff) {
		this.sc = sc;
		this.cert = sc.getCertificate();
		this.handoff = handoff;
		int extraElement = (handoff==null) ? 0 : 1;
		Sexp[] sexpary = new Sexp[3+extraElement];
		sexpary[0] = new SexpString(Proof.LABEL);
		sexpary[1] = new SexpString(SignedCertificateProof.LABEL);
		sexpary[2] = sc.getSrep();
		if (handoff!=null) {
			sexpary[3] = handoff.getSrep();
		}
		srep = new SexpList(sexpary);
	}

	public Subject getSubject() {
		return cert.getSubject();
	}

	public SDSIPrincipal getIssuer() {
		if (cert instanceof Def) {
			// irritating Def class 'hides' name in a different
			// method call; apparently Morcos considers the issuer
			// of a cert to be the guy who signed it, not the guy
			// getting spoken for. So "issuer" has a subtle and
			// worrisomely-sloppy definition. TODO in a rewrite of
			// this architecture, appropriate language should be
			// chosen to be ambiguous.
			return ((Def) cert).getName();
		} else {
			return cert.getIssuer();
		}
	}

	public Tag getTag() {
		if (cert instanceof Auth) {
			return ((Auth) cert).getTag();
		} else {
			return Tag.getTagStar();
		}
	}

	/**
	 * verify that the proof steps are indeed valid, and that they
	 * combine as advertised to show the claimed result
	 */
	protected void directVerify()
		throws InvalidProofException {
		// Calling verify() on a SignedCertificate ensures that
		// the issuer is either the one who signed it (no handoff),
		// or the signer quoting others (a limited form of handoff).
		// Other forms of handoff are not yet supported.

		sc.verify();
	}

	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:
				addToMe.add(sc);
				break;
			case Proof.PROOFS:
				addToMe.add(this);
				break;
			default:
				throw new RuntimeException("undefined what.");
		}
		if (handoff!=null) {
			handoff.preorder(addToMe, what);
		}
	}

	public Proof getChildProof(int i) {
		if (i==0) {
			return handoff;
		} else {
			return null;
		}
	}

	public Proof substituteProof(int i, Proof subProof) {
		Proof nHandoff = (i==0) ? subProof : handoff;
		return new SignedCertificateProof(sc, nHandoff);
	}
}
