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

package org.bouncycastle.crypto.generators;

import java.util.HashSet;
import java.io.ByteArrayOutputStream;
import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.Strings;
import java.util.Set;

public class OpenBSDBCrypt
{
    private static final byte[] encodingTable;
    private static final byte[] decodingTable;
    private static final String defaultVersion = "2y";
    private static final Set<String> allowedVersions;
    
    private OpenBSDBCrypt() {
    }
    
    public static String generate(final char[] array, final byte[] array2, final int n) {
        return generate("2y", array, array2, n);
    }
    
    public static String generate(final byte[] array, final byte[] array2, final int n) {
        return generate("2y", array, array2, n);
    }
    
    public static String generate(final String s, final char[] array, final byte[] array2, final int n) {
        if (array == null) {
            throw new IllegalArgumentException("Password required.");
        }
        return doGenerate(s, Strings.toUTF8ByteArray(array), array2, n);
    }
    
    public static String generate(final String s, final byte[] array, final byte[] array2, final int n) {
        if (array == null) {
            throw new IllegalArgumentException("Password required.");
        }
        return doGenerate(s, Arrays.clone(array), array2, n);
    }
    
    private static String doGenerate(final String str, final byte[] array, final byte[] array2, final int n) {
        if (!OpenBSDBCrypt.allowedVersions.contains(str)) {
            throw new IllegalArgumentException("Version " + str + " is not accepted by this implementation.");
        }
        if (array2 == null) {
            throw new IllegalArgumentException("Salt required.");
        }
        if (array2.length != 16) {
            throw new DataLengthException("16 byte salt required: " + array2.length);
        }
        if (n < 4 || n > 31) {
            throw new IllegalArgumentException("Invalid cost factor.");
        }
        final byte[] array3 = new byte[(array.length >= 72) ? 72 : (array.length + 1)];
        if (array3.length > array.length) {
            System.arraycopy(array, 0, array3, 0, array.length);
        }
        else {
            System.arraycopy(array, 0, array3, 0, array3.length);
        }
        Arrays.fill(array, (byte)0);
        final String bcryptString = createBcryptString(str, array3, array2, n);
        Arrays.fill(array3, (byte)0);
        return bcryptString;
    }
    
    public static boolean checkPassword(final String s, final char[] array) {
        if (array == null) {
            throw new IllegalArgumentException("Missing password.");
        }
        return doCheckPassword(s, Strings.toUTF8ByteArray(array));
    }
    
    public static boolean checkPassword(final String s, final byte[] array) {
        if (array == null) {
            throw new IllegalArgumentException("Missing password.");
        }
        return doCheckPassword(s, Arrays.clone(array));
    }
    
    private static boolean doCheckPassword(final String s, final byte[] array) {
        if (s == null) {
            throw new IllegalArgumentException("Missing bcryptString.");
        }
        if (s.charAt(1) != '2') {
            throw new IllegalArgumentException("not a Bcrypt string");
        }
        final int length = s.length();
        if (length != 60 && (length != 59 || s.charAt(2) != '$')) {
            throw new DataLengthException("Bcrypt String length: " + length + ", 60 required.");
        }
        if (s.charAt(2) == '$') {
            if (s.charAt(0) != '$' || s.charAt(5) != '$') {
                throw new IllegalArgumentException("Invalid Bcrypt String format.");
            }
        }
        else if (s.charAt(0) != '$' || s.charAt(3) != '$' || s.charAt(6) != '$') {
            throw new IllegalArgumentException("Invalid Bcrypt String format.");
        }
        String str;
        int beginIndex;
        if (s.charAt(2) == '$') {
            str = s.substring(1, 2);
            beginIndex = 3;
        }
        else {
            str = s.substring(1, 3);
            beginIndex = 4;
        }
        if (!OpenBSDBCrypt.allowedVersions.contains(str)) {
            throw new IllegalArgumentException("Bcrypt version '" + str + "' is not supported by this implementation");
        }
        final String substring = s.substring(beginIndex, beginIndex + 2);
        int int1;
        try {
            int1 = Integer.parseInt(substring);
        }
        catch (final NumberFormatException ex) {
            throw new IllegalArgumentException("Invalid cost factor: " + substring);
        }
        if (int1 < 4 || int1 > 31) {
            throw new IllegalArgumentException("Invalid cost factor: " + int1 + ", 4 < cost < 31 expected.");
        }
        return Strings.constantTimeAreEqual(s, doGenerate(str, array, decodeSaltString(s.substring(s.lastIndexOf(36) + 1, length - 31)), int1));
    }
    
    private static String createBcryptString(final String s, final byte[] array, final byte[] array2, final int n) {
        if (!OpenBSDBCrypt.allowedVersions.contains(s)) {
            throw new IllegalArgumentException("Version " + s + " is not accepted by this implementation.");
        }
        final StringBuilder sb = new StringBuilder(60);
        sb.append('$');
        sb.append(s);
        sb.append('$');
        sb.append((n < 10) ? ("0" + n) : Integer.toString(n));
        sb.append('$');
        encodeData(sb, array2);
        encodeData(sb, BCrypt.generate(array, array2, n));
        return sb.toString();
    }
    
    private static void encodeData(final StringBuilder sb, byte[] array) {
        if (array.length != 24 && array.length != 16) {
            throw new DataLengthException("Invalid length: " + array.length + ", 24 for key or 16 for salt expected");
        }
        int n = 0;
        if (array.length == 16) {
            n = 1;
            final byte[] array2 = new byte[18];
            System.arraycopy(array, 0, array2, 0, array.length);
            array = array2;
        }
        else {
            array[array.length - 1] = 0;
        }
        for (int length = array.length, i = 0; i < length; i += 3) {
            final int n2 = array[i] & 0xFF;
            final int n3 = array[i + 1] & 0xFF;
            final int n4 = array[i + 2] & 0xFF;
            sb.append((char)OpenBSDBCrypt.encodingTable[n2 >>> 2 & 0x3F]);
            sb.append((char)OpenBSDBCrypt.encodingTable[(n2 << 4 | n3 >>> 4) & 0x3F]);
            sb.append((char)OpenBSDBCrypt.encodingTable[(n3 << 2 | n4 >>> 6) & 0x3F]);
            sb.append((char)OpenBSDBCrypt.encodingTable[n4 & 0x3F]);
        }
        if (n == 1) {
            sb.setLength();
        }
        else {
            sb.setLength();
        }
    }
    
    private static byte[] decodeSaltString(final String s) {
        final char[] charArray = s.toCharArray();
        final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(16);
        if (charArray.length != 22) {
            throw new DataLengthException("Invalid base64 salt length: " + charArray.length + " , 22 required.");
        }
        for (int i = 0; i < charArray.length; ++i) {
            final char j = charArray[i];
            if (j > 'z' || j < '.' || (j > '9' && j < 'A')) {
                throw new IllegalArgumentException("Salt string contains invalid character: " + (int)j);
            }
        }
        final char[] array = new char[24];
        System.arraycopy(charArray, 0, array, 0, charArray.length);
        final char[] array2 = array;
        for (int length = array2.length, k = 0; k < length; k += 4) {
            final byte b = OpenBSDBCrypt.decodingTable[array2[k]];
            final byte b2 = OpenBSDBCrypt.decodingTable[array2[k + 1]];
            final byte b3 = OpenBSDBCrypt.decodingTable[array2[k + 2]];
            final byte b4 = OpenBSDBCrypt.decodingTable[array2[k + 3]];
            byteArrayOutputStream.write(b << 2 | b2 >> 4);
            byteArrayOutputStream.write(b2 << 4 | b3 >> 2);
            byteArrayOutputStream.write(b3 << 6 | b4);
        }
        final byte[] byteArray = byteArrayOutputStream.toByteArray();
        final byte[] array3 = new byte[16];
        System.arraycopy(byteArray, 0, array3, 0, array3.length);
        return array3;
    }
    
    static {
        encodingTable = new byte[] { 46, 47, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57 };
        decodingTable = new byte[128];
        (allowedVersions = new HashSet<String>()).add("2");
        OpenBSDBCrypt.allowedVersions.add("2x");
        OpenBSDBCrypt.allowedVersions.add("2a");
        OpenBSDBCrypt.allowedVersions.add("2y");
        OpenBSDBCrypt.allowedVersions.add("2b");
        for (int i = 0; i < OpenBSDBCrypt.decodingTable.length; ++i) {
            OpenBSDBCrypt.decodingTable[i] = -1;
        }
        for (int j = 0; j < OpenBSDBCrypt.encodingTable.length; ++j) {
            OpenBSDBCrypt.decodingTable[OpenBSDBCrypt.encodingTable[j]] = (byte)j;
        }
    }
}
