// 
// Decompiled by Procyon v0.6.0
// 

package org.bouncycastle.crypto.agreement.ecjpake;

import org.bouncycastle.util.Exceptions;
import org.bouncycastle.crypto.CryptoException;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.crypto.CryptoServicesRegistrar;
import org.bouncycastle.crypto.digests.SHA256Digest;
import org.bouncycastle.math.ec.ECPoint;
import java.math.BigInteger;
import org.bouncycastle.math.ec.ECCurve;
import java.security.SecureRandom;
import org.bouncycastle.crypto.Digest;

public class ECJPAKEParticipant
{
    public static final int STATE_INITIALIZED = 0;
    public static final int STATE_ROUND_1_CREATED = 10;
    public static final int STATE_ROUND_1_VALIDATED = 20;
    public static final int STATE_ROUND_2_CREATED = 30;
    public static final int STATE_ROUND_2_VALIDATED = 40;
    public static final int STATE_KEY_CALCULATED = 50;
    public static final int STATE_ROUND_3_CREATED = 60;
    public static final int STATE_ROUND_3_VALIDATED = 70;
    private final String participantId;
    private char[] password;
    private final Digest digest;
    private final SecureRandom random;
    private String partnerParticipantId;
    private ECCurve.AbstractFp ecCurve;
    private BigInteger q;
    private BigInteger h;
    private BigInteger n;
    private ECPoint g;
    private BigInteger x1;
    private BigInteger x2;
    private ECPoint gx1;
    private ECPoint gx2;
    private ECPoint gx3;
    private ECPoint gx4;
    private ECPoint b;
    private int state;
    
    public ECJPAKEParticipant(final String s, final char[] array) {
        this(s, array, ECJPAKECurves.NIST_P256);
    }
    
    public ECJPAKEParticipant(final String s, final char[] array, final ECJPAKECurve ecjpakeCurve) {
        this(s, array, ecjpakeCurve, SHA256Digest.newInstance(), CryptoServicesRegistrar.getSecureRandom());
    }
    
    public ECJPAKEParticipant(final String participantId, final char[] array, final ECJPAKECurve ecjpakeCurve, final Digest digest, final SecureRandom random) {
        ECJPAKEUtil.validateNotNull(participantId, "participantId");
        ECJPAKEUtil.validateNotNull(array, "password");
        ECJPAKEUtil.validateNotNull(ecjpakeCurve, "curve params");
        ECJPAKEUtil.validateNotNull(digest, "digest");
        ECJPAKEUtil.validateNotNull(random, "random");
        if (array.length == 0) {
            throw new IllegalArgumentException("Password must not be empty.");
        }
        this.participantId = participantId;
        this.password = Arrays.copyOf(array, array.length);
        this.ecCurve = ecjpakeCurve.getCurve();
        this.g = ecjpakeCurve.getG();
        this.h = ecjpakeCurve.getH();
        this.n = ecjpakeCurve.getN();
        this.q = ecjpakeCurve.getQ();
        this.digest = digest;
        this.random = random;
        this.state = 0;
    }
    
    public int getState() {
        return this.state;
    }
    
    public ECJPAKERound1Payload createRound1PayloadToSend() {
        if (this.state >= 10) {
            throw new IllegalStateException("Round1 payload already created for " + this.participantId);
        }
        this.x1 = ECJPAKEUtil.generateX1(this.n, this.random);
        this.x2 = ECJPAKEUtil.generateX1(this.n, this.random);
        this.gx1 = ECJPAKEUtil.calculateGx(this.g, this.x1);
        this.gx2 = ECJPAKEUtil.calculateGx(this.g, this.x2);
        final ECSchnorrZKP calculateZeroKnowledgeProof = ECJPAKEUtil.calculateZeroKnowledgeProof(this.g, this.n, this.x1, this.gx1, this.digest, this.participantId, this.random);
        final ECSchnorrZKP calculateZeroKnowledgeProof2 = ECJPAKEUtil.calculateZeroKnowledgeProof(this.g, this.n, this.x2, this.gx2, this.digest, this.participantId, this.random);
        this.state = 10;
        return new ECJPAKERound1Payload(this.participantId, this.gx1, this.gx2, calculateZeroKnowledgeProof, calculateZeroKnowledgeProof2);
    }
    
    public void validateRound1PayloadReceived(final ECJPAKERound1Payload ecjpakeRound1Payload) throws CryptoException {
        if (this.state >= 20) {
            throw new IllegalStateException("Validation already attempted for round1 payload for" + this.participantId);
        }
        this.partnerParticipantId = ecjpakeRound1Payload.getParticipantId();
        this.gx3 = ecjpakeRound1Payload.getGx1();
        this.gx4 = ecjpakeRound1Payload.getGx2();
        final ECSchnorrZKP knowledgeProofForX1 = ecjpakeRound1Payload.getKnowledgeProofForX1();
        final ECSchnorrZKP knowledgeProofForX2 = ecjpakeRound1Payload.getKnowledgeProofForX2();
        ECJPAKEUtil.validateParticipantIdsDiffer(this.participantId, ecjpakeRound1Payload.getParticipantId());
        ECJPAKEUtil.validateZeroKnowledgeProof(this.g, this.gx3, knowledgeProofForX1, this.q, this.n, this.ecCurve, this.h, ecjpakeRound1Payload.getParticipantId(), this.digest);
        ECJPAKEUtil.validateZeroKnowledgeProof(this.g, this.gx4, knowledgeProofForX2, this.q, this.n, this.ecCurve, this.h, ecjpakeRound1Payload.getParticipantId(), this.digest);
        this.state = 20;
    }
    
    public ECJPAKERound2Payload createRound2PayloadToSend() {
        if (this.state >= 30) {
            throw new IllegalStateException("Round2 payload already created for " + this.participantId);
        }
        if (this.state < 20) {
            throw new IllegalStateException("Round1 payload must be validated prior to creating Round2 payload for " + this.participantId);
        }
        final ECPoint calculateGA = ECJPAKEUtil.calculateGA(this.gx1, this.gx3, this.gx4);
        final BigInteger calculateX2s = ECJPAKEUtil.calculateX2s(this.n, this.x2, this.calculateS());
        final ECPoint calculateA = ECJPAKEUtil.calculateA(calculateGA, calculateX2s);
        final ECSchnorrZKP calculateZeroKnowledgeProof = ECJPAKEUtil.calculateZeroKnowledgeProof(calculateGA, this.n, calculateX2s, calculateA, this.digest, this.participantId, this.random);
        this.state = 30;
        return new ECJPAKERound2Payload(this.participantId, calculateA, calculateZeroKnowledgeProof);
    }
    
    public void validateRound2PayloadReceived(final ECJPAKERound2Payload ecjpakeRound2Payload) throws CryptoException {
        if (this.state >= 40) {
            throw new IllegalStateException("Validation already attempted for round2 payload for" + this.participantId);
        }
        if (this.state < 20) {
            throw new IllegalStateException("Round1 payload must be validated prior to validating Round2 payload for " + this.participantId);
        }
        final ECPoint calculateGA = ECJPAKEUtil.calculateGA(this.gx3, this.gx1, this.gx2);
        this.b = ecjpakeRound2Payload.getA();
        final ECSchnorrZKP knowledgeProofForX2s = ecjpakeRound2Payload.getKnowledgeProofForX2s();
        ECJPAKEUtil.validateParticipantIdsDiffer(this.participantId, ecjpakeRound2Payload.getParticipantId());
        ECJPAKEUtil.validateParticipantIdsEqual(this.partnerParticipantId, ecjpakeRound2Payload.getParticipantId());
        ECJPAKEUtil.validateZeroKnowledgeProof(calculateGA, this.b, knowledgeProofForX2s, this.q, this.n, this.ecCurve, this.h, ecjpakeRound2Payload.getParticipantId(), this.digest);
        this.state = 40;
    }
    
    public BigInteger calculateKeyingMaterial() {
        if (this.state >= 50) {
            throw new IllegalStateException("Key already calculated for " + this.participantId);
        }
        if (this.state < 40) {
            throw new IllegalStateException("Round2 payload must be validated prior to creating key for " + this.participantId);
        }
        final BigInteger calculateS = this.calculateS();
        Arrays.fill(this.password, '\0');
        this.password = null;
        final BigInteger calculateKeyingMaterial = ECJPAKEUtil.calculateKeyingMaterial(this.n, this.gx4, this.x2, calculateS, this.b);
        this.x1 = null;
        this.x2 = null;
        this.b = null;
        this.state = 50;
        return calculateKeyingMaterial;
    }
    
    public ECJPAKERound3Payload createRound3PayloadToSend(final BigInteger bigInteger) {
        if (this.state >= 60) {
            throw new IllegalStateException("Round3 payload already created for " + this.participantId);
        }
        if (this.state < 50) {
            throw new IllegalStateException("Keying material must be calculated prior to creating Round3 payload for " + this.participantId);
        }
        final BigInteger calculateMacTag = ECJPAKEUtil.calculateMacTag(this.participantId, this.partnerParticipantId, this.gx1, this.gx2, this.gx3, this.gx4, bigInteger, this.digest);
        this.state = 60;
        return new ECJPAKERound3Payload(this.participantId, calculateMacTag);
    }
    
    public void validateRound3PayloadReceived(final ECJPAKERound3Payload ecjpakeRound3Payload, final BigInteger bigInteger) throws CryptoException {
        if (this.state >= 70) {
            throw new IllegalStateException("Validation already attempted for round3 payload for" + this.participantId);
        }
        if (this.state < 50) {
            throw new IllegalStateException("Keying material must be calculated validated prior to validating Round3 payload for " + this.participantId);
        }
        ECJPAKEUtil.validateParticipantIdsDiffer(this.participantId, ecjpakeRound3Payload.getParticipantId());
        ECJPAKEUtil.validateParticipantIdsEqual(this.partnerParticipantId, ecjpakeRound3Payload.getParticipantId());
        ECJPAKEUtil.validateMacTag(this.participantId, this.partnerParticipantId, this.gx1, this.gx2, this.gx3, this.gx4, bigInteger, this.digest, ecjpakeRound3Payload.getMacTag());
        this.gx1 = null;
        this.gx2 = null;
        this.gx3 = null;
        this.gx4 = null;
        this.state = 70;
    }
    
    private BigInteger calculateS() {
        try {
            return ECJPAKEUtil.calculateS(this.n, this.password);
        }
        catch (final CryptoException ex) {
            throw Exceptions.illegalStateException(ex.getMessage(), ex);
        }
    }
}
