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

package com.google.crypto.tink.subtle;

import javax.crypto.BadPaddingException;
import java.util.Arrays;
import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.security.GeneralSecurityException;
import javax.crypto.SecretKey;
import com.google.crypto.tink.KeyWrap;

@Deprecated
public class Kwp implements KeyWrap
{
    private final SecretKey aesKey;
    static final int MIN_WRAP_KEY_SIZE = 16;
    static final int MAX_WRAP_KEY_SIZE = 4096;
    static final int ROUNDS = 6;
    static final byte[] PREFIX;
    
    public Kwp(final byte[] key) throws GeneralSecurityException {
        if (key.length != 16 && key.length != 32) {
            throw new GeneralSecurityException("Unsupported key length");
        }
        this.aesKey = new SecretKeySpec(key, "AES");
    }
    
    private int wrappingSize(final int inputSize) {
        final int paddingSize = 7 - (inputSize + 7) % 8;
        return inputSize + paddingSize + 8;
    }
    
    private byte[] computeW(final byte[] iv, final byte[] key) throws GeneralSecurityException {
        if (key.length <= 8 || key.length > 2147483631 || iv.length != 8) {
            throw new GeneralSecurityException("computeW called with invalid parameters");
        }
        final byte[] data = new byte[this.wrappingSize(key.length)];
        System.arraycopy(iv, 0, data, 0, iv.length);
        System.arraycopy(key, 0, data, 8, key.length);
        final int blocks = data.length / 8 - 1;
        final Cipher aes = EngineFactory.CIPHER.getInstance("AES/ECB/NoPadding");
        aes.init(1, this.aesKey);
        final byte[] block = new byte[16];
        System.arraycopy(data, 0, block, 0, 8);
        for (int i = 0; i < 6; ++i) {
            for (int j = 0; j < blocks; ++j) {
                System.arraycopy(data, 8 * (j + 1), block, 8, 8);
                final int length = aes.doFinal(block, 0, 16, block);
                assert length == 16;
                int roundConst = i * blocks + j + 1;
                for (int b = 0; b < 4; ++b) {
                    final byte[] array = block;
                    final int n = 7 - b;
                    array[n] ^= (byte)(roundConst & 0xFF);
                    roundConst >>>= 8;
                }
                System.arraycopy(block, 8, data, 8 * (j + 1), 8);
            }
        }
        System.arraycopy(block, 0, data, 0, 8);
        return data;
    }
    
    private byte[] invertW(final byte[] wrapped) throws GeneralSecurityException {
        if (wrapped.length < 24 || wrapped.length % 8 != 0) {
            throw new GeneralSecurityException("Incorrect data size");
        }
        final byte[] data = Arrays.copyOf(wrapped, wrapped.length);
        final int blocks = data.length / 8 - 1;
        final Cipher aes = EngineFactory.CIPHER.getInstance("AES/ECB/NoPadding");
        aes.init(2, this.aesKey);
        final byte[] block = new byte[16];
        System.arraycopy(data, 0, block, 0, 8);
        for (int i = 5; i >= 0; --i) {
            for (int j = blocks - 1; j >= 0; --j) {
                System.arraycopy(data, 8 * (j + 1), block, 8, 8);
                int roundConst = i * blocks + j + 1;
                for (int b = 0; b < 4; ++b) {
                    final byte[] array = block;
                    final int n = 7 - b;
                    array[n] ^= (byte)(roundConst & 0xFF);
                    roundConst >>>= 8;
                }
                final int length = aes.doFinal(block, 0, 16, block);
                assert length == 16;
                System.arraycopy(block, 8, data, 8 * (j + 1), 8);
            }
        }
        System.arraycopy(block, 0, data, 0, 8);
        return data;
    }
    
    @Override
    public byte[] wrap(final byte[] data) throws GeneralSecurityException {
        if (data.length < 16) {
            throw new GeneralSecurityException("Key size of key to wrap too small");
        }
        if (data.length > 4096) {
            throw new GeneralSecurityException("Key size of key to wrap too large");
        }
        final byte[] iv = new byte[8];
        System.arraycopy(Kwp.PREFIX, 0, iv, 0, Kwp.PREFIX.length);
        for (int i = 0; i < 4; ++i) {
            iv[4 + i] = (byte)(data.length >> 8 * (3 - i) & 0xFF);
        }
        return this.computeW(iv, data);
    }
    
    @Override
    public byte[] unwrap(final byte[] data) throws GeneralSecurityException {
        if (data.length < this.wrappingSize(16)) {
            throw new GeneralSecurityException("Wrapped key size is too small");
        }
        if (data.length > this.wrappingSize(4096)) {
            throw new GeneralSecurityException("Wrapped key size is too large");
        }
        if (data.length % 8 != 0) {
            throw new GeneralSecurityException("Wrapped key size must be a multiple of 8 bytes");
        }
        final byte[] unwrapped = this.invertW(data);
        boolean ok = true;
        for (int i = 0; i < 4; ++i) {
            if (Kwp.PREFIX[i] != unwrapped[i]) {
                ok = false;
            }
        }
        int encodedSize = 0;
        for (int j = 4; j < 8; ++j) {
            encodedSize = (encodedSize << 8) + (unwrapped[j] & 0xFF);
        }
        if (this.wrappingSize(encodedSize) != unwrapped.length) {
            ok = false;
        }
        else {
            for (int k = 8 + encodedSize; k < unwrapped.length; ++k) {
                if (unwrapped[k] != 0) {
                    ok = false;
                }
            }
        }
        if (ok) {
            return Arrays.copyOfRange(unwrapped, 8, 8 + encodedSize);
        }
        throw new BadPaddingException("Invalid padding");
    }
    
    static {
        PREFIX = new byte[] { -90, 89, 89, -90 };
    }
}
