

package org.irmacard.credentials.idemix.proofs;

import java.math.BigInteger;
import java.util.Arrays;
import java.util.List;

import de.henku.jpaillier.KeyPair;
import org.irmacard.credentials.idemix.IdemixPublicKey;
import org.irmacard.credentials.idemix.IdemixSystemParameters;
import org.irmacard.credentials.idemix.util.Crypto;

public class ProofP implements Proof {
	private BigInteger P;
	private BigInteger c;
	private BigInteger s_response;

	public ProofP(BigInteger P, BigInteger c, BigInteger s_response) {
		this.P = P;
		this.c = c;
		this.s_response = s_response;
	}

	@Override
	public boolean verify(IdemixPublicKey pk, BigInteger context,
			BigInteger nonce) {
		return verify(pk, context, nonce, null);
	}

	@Override
	public boolean verify(IdemixPublicKey pk, BigInteger context,
			BigInteger nonce, BigInteger challenge) {
		IdemixSystemParameters params = pk.getSystemParameters();

		// Recalculate hash
		BigInteger c_prime = challenge;
		if (c_prime == null) {
			BigInteger P_commit = reconstructP_commit(pk);
			c_prime = Crypto.sha256Hash(Crypto.asn1Encode(context, P, P_commit, nonce));
		}

		boolean matched = c.compareTo(c_prime) == 0;

		if(!matched) {
			System.out.println("Hash doesn't match");
		}

		return matched;
	}

	@Override
	public List<BigInteger> getChallengeContribution(IdemixPublicKey pk) {
		return Arrays.asList(P, reconstructP_commit(pk));
	}

	@Override
	public IdemixPublicKey extractPublicKey() {
		return null;
	}

	@Override
	public BigInteger get_c() {
		return c;
	}

	@Override
	public BigInteger getSecretKeyResponse() {
		return s_response;
	}

	public void decrypt(KeyPair keyPair) {
		s_response = keyPair.decrypt(s_response);
	}

	public BigInteger getP() {
		return P;
	}

	public BigInteger reconstructP_commit(IdemixPublicKey pk) {
		BigInteger n = pk.getModulus();

		// Reconstruct U_commit
		// U_commit = P^{-c} * R_0^{s_response}
		BigInteger Uc = P.modPow(this.c.negate(), n);
		BigInteger R0s = pk.getGeneratorR(0).modPow(this.s_response, n);

		return Uc.multiply(R0s).mod(n);
	}

	public ProofP mergeProofP(ProofP p, IdemixPublicKey pk) {
		throw new RuntimeException("Can't merge proofP's with proofP's");
	}
}
