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

package org.bouncycastle.cert.crmf;

import org.bouncycastle.operator.RuntimeOperatorException;
import java.io.OutputStream;
import org.bouncycastle.operator.GenericKey;
import java.io.ByteArrayOutputStream;
import org.bouncycastle.util.Strings;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.cmp.CMPObjectIdentifiers;
import org.bouncycastle.operator.MacCalculator;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.DERNull;
import org.bouncycastle.asn1.iana.IANAObjectIdentifiers;
import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
import org.bouncycastle.asn1.cmp.PBMParameter;
import java.security.SecureRandom;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.operator.PBEMacCalculatorProvider;

public class PKMACBuilder implements PBEMacCalculatorProvider
{
    private AlgorithmIdentifier owf;
    private int iterationCount;
    private AlgorithmIdentifier mac;
    private int saltLength;
    private SecureRandom random;
    private PKMACValuesCalculator calculator;
    private PBMParameter parameters;
    private int maxIterations;
    
    public PKMACBuilder(final PKMACValuesCalculator pkmacValuesCalculator) {
        this(new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1), 1000, new AlgorithmIdentifier(IANAObjectIdentifiers.hmacSHA1, DERNull.INSTANCE), pkmacValuesCalculator);
    }
    
    public PKMACBuilder(final PKMACValuesCalculator calculator, final int maxIterations) {
        this.saltLength = 20;
        this.maxIterations = maxIterations;
        this.calculator = calculator;
    }
    
    private PKMACBuilder(final AlgorithmIdentifier owf, final int iterationCount, final AlgorithmIdentifier mac, final PKMACValuesCalculator calculator) {
        this.saltLength = 20;
        this.owf = owf;
        this.iterationCount = iterationCount;
        this.mac = mac;
        this.calculator = calculator;
    }
    
    public PKMACBuilder setSaltLength(final int saltLength) {
        if (saltLength < 8) {
            throw new IllegalArgumentException("salt length must be at least 8 bytes");
        }
        this.saltLength = saltLength;
        return this;
    }
    
    public PKMACBuilder setIterationCount(final int iterationCount) {
        if (iterationCount < 100) {
            throw new IllegalArgumentException("iteration count must be at least 100");
        }
        this.checkIterationCountCeiling(iterationCount);
        this.iterationCount = iterationCount;
        return this;
    }
    
    public PKMACBuilder setSecureRandom(final SecureRandom random) {
        this.random = random;
        return this;
    }
    
    public PKMACBuilder setParameters(final PBMParameter parameters) {
        this.checkIterationCountCeiling(parameters.getIterationCount().intValueExact());
        this.parameters = parameters;
        return this;
    }
    
    @Override
    public MacCalculator get(final AlgorithmIdentifier algorithmIdentifier, final char[] array) throws OperatorCreationException {
        if (!CMPObjectIdentifiers.passwordBasedMac.equals(algorithmIdentifier.getAlgorithm())) {
            throw new OperatorCreationException("protection algorithm not mac based");
        }
        this.setParameters(PBMParameter.getInstance(algorithmIdentifier.getParameters()));
        try {
            return this.build(array);
        }
        catch (final CRMFException ex) {
            throw new OperatorCreationException(ex.getMessage(), ex.getCause());
        }
    }
    
    public MacCalculator build(final char[] array) throws CRMFException {
        PBMParameter pbmParameter = this.parameters;
        if (pbmParameter == null) {
            pbmParameter = this.genParameters();
        }
        return this.genCalculator(pbmParameter, array);
    }
    
    private void checkIterationCountCeiling(final int i) {
        if (this.maxIterations > 0 && i > this.maxIterations) {
            throw new IllegalArgumentException("iteration count exceeds limit (" + i + " > " + this.maxIterations + ")");
        }
    }
    
    private MacCalculator genCalculator(final PBMParameter pbmParameter, final char[] array) throws CRMFException {
        final byte[] utf8ByteArray = Strings.toUTF8ByteArray(array);
        final byte[] octets = pbmParameter.getSalt().getOctets();
        byte[] calculateDigest = new byte[utf8ByteArray.length + octets.length];
        System.arraycopy(utf8ByteArray, 0, calculateDigest, 0, utf8ByteArray.length);
        System.arraycopy(octets, 0, calculateDigest, utf8ByteArray.length, octets.length);
        this.calculator.setup(pbmParameter.getOwf(), pbmParameter.getMac());
        int intValueExact = pbmParameter.getIterationCount().intValueExact();
        do {
            calculateDigest = this.calculator.calculateDigest(calculateDigest);
        } while (--intValueExact > 0);
        return new MacCalculator() {
            ByteArrayOutputStream bOut = new ByteArrayOutputStream();
            
            @Override
            public AlgorithmIdentifier getAlgorithmIdentifier() {
                return new AlgorithmIdentifier(CMPObjectIdentifiers.passwordBasedMac, pbmParameter);
            }
            
            @Override
            public GenericKey getKey() {
                return new GenericKey(this.getAlgorithmIdentifier(), calculateDigest);
            }
            
            @Override
            public OutputStream getOutputStream() {
                return this.bOut;
            }
            
            @Override
            public byte[] getMac() {
                try {
                    return PKMACBuilder.this.calculator.calculateMac(calculateDigest, this.bOut.toByteArray());
                }
                catch (final CRMFException ex) {
                    throw new RuntimeOperatorException("exception calculating mac: " + ex.getMessage(), ex);
                }
            }
        };
    }
    
    private PBMParameter genParameters() {
        final byte[] bytes = new byte[this.saltLength];
        if (this.random == null) {
            this.random = new SecureRandom();
        }
        this.random.nextBytes(bytes);
        return new PBMParameter(bytes, this.owf, this.iterationCount, this.mac);
    }
}
