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

package com.nimbusds.jose.crypto.impl;

import com.nimbusds.jose.util.IntegerUtils;
import javax.crypto.Mac;
import java.security.GeneralSecurityException;
import java.security.spec.InvalidKeySpecException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.spec.SecretKeySpec;
import java.security.spec.KeySpec;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.nio.charset.StandardCharsets;
import com.nimbusds.jose.util.ByteUtils;
import javax.crypto.SecretKey;
import java.security.Provider;
import java.io.IOException;
import java.io.ByteArrayOutputStream;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.util.StandardCharset;
import com.nimbusds.jose.JWEAlgorithm;

public class PBKDF2
{
    public static final int MIN_SALT_LENGTH = 8;
    static final byte[] ZERO_BYTE;
    static final long MAX_DERIVED_KEY_LENGTH = 4294967295L;
    
    public static byte[] formatSalt(final JWEAlgorithm alg, final byte[] salt) throws JOSEException {
        final byte[] algBytes = alg.toString().getBytes(StandardCharset.UTF_8);
        if (salt == null) {
            throw new JOSEException("The salt must not be null");
        }
        if (salt.length < 8) {
            throw new JOSEException("The salt must be at least 8 bytes long");
        }
        final ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            out.write(algBytes);
            out.write(PBKDF2.ZERO_BYTE);
            out.write(salt);
        }
        catch (final IOException e) {
            throw new JOSEException(e.getMessage(), e);
        }
        return out.toByteArray();
    }
    
    public static SecretKey deriveKey(final byte[] password, final byte[] formattedSalt, final int iterationCount, final PRFParams prfParams, final Provider jcaProvider) throws JOSEException {
        if (formattedSalt == null) {
            throw new JOSEException("The formatted salt must not be null");
        }
        if (iterationCount < 1) {
            throw new JOSEException("The iteration count must be greater than 0");
        }
        final int keyLengthInBits = ByteUtils.bitLength(prfParams.getDerivedKeyByteLength());
        final PBEKeySpec spec = new PBEKeySpec(new String(password, StandardCharsets.UTF_8).toCharArray(), formattedSalt, iterationCount, keyLengthInBits);
        try {
            SecretKeyFactory skf;
            if (jcaProvider != null) {
                skf = SecretKeyFactory.getInstance("PBKDF2With" + prfParams.getMACAlgorithm(), jcaProvider);
            }
            else {
                skf = SecretKeyFactory.getInstance("PBKDF2With" + prfParams.getMACAlgorithm());
            }
            return new SecretKeySpec(skf.generateSecret(spec).getEncoded(), "AES");
        }
        catch (final NoSuchAlgorithmException | InvalidKeySpecException ex) {
            throw new JOSEException(ex.getLocalizedMessage(), ex);
        }
    }
    
    static byte[] extractBlock(final byte[] formattedSalt, final int iterationCount, final int blockIndex, final Mac prf) throws JOSEException {
        if (formattedSalt == null) {
            throw new JOSEException("The formatted salt must not be null");
        }
        if (iterationCount < 1) {
            throw new JOSEException("The iteration count must be greater than 0");
        }
        byte[] lastU = null;
        byte[] xorU = null;
        for (int i = 1; i <= iterationCount; ++i) {
            byte[] currentU;
            if (i == 1) {
                final byte[] inputBytes = ByteUtils.concat(new byte[][] { formattedSalt, IntegerUtils.toBytes(blockIndex) });
                currentU = (xorU = prf.doFinal(inputBytes));
            }
            else {
                currentU = prf.doFinal(lastU);
                for (int j = 0; j < currentU.length; ++j) {
                    xorU[j] ^= currentU[j];
                }
            }
            lastU = currentU;
        }
        return xorU;
    }
    
    private PBKDF2() {
    }
    
    static {
        ZERO_BYTE = new byte[] { 0 };
    }
}
