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

package com.google.crypto.tink.aead.internal;

import java.nio.ByteOrder;
import com.google.crypto.tink.subtle.Bytes;
import java.security.GeneralSecurityException;
import java.nio.ByteBuffer;
import java.security.InvalidKeyException;

abstract class InsecureNonceChaCha20Base
{
    int[] key;
    private final int initialCounter;
    
    public InsecureNonceChaCha20Base(final byte[] key, final int initialCounter) throws InvalidKeyException {
        if (key.length != 32) {
            throw new InvalidKeyException("The key length in bytes must be 32.");
        }
        this.key = ChaCha20Util.toIntArray(key);
        this.initialCounter = initialCounter;
    }
    
    abstract int[] createInitialState(final int[] nonce, final int counter);
    
    abstract int nonceSizeInBytes();
    
    public byte[] encrypt(final byte[] nonce, final byte[] plaintext) throws GeneralSecurityException {
        final ByteBuffer ciphertext = ByteBuffer.allocate(plaintext.length);
        this.encrypt(ciphertext, nonce, plaintext);
        return ciphertext.array();
    }
    
    public void encrypt(final ByteBuffer output, final byte[] nonce, final byte[] plaintext) throws GeneralSecurityException {
        if (output.remaining() < plaintext.length) {
            throw new IllegalArgumentException("Given ByteBuffer output is too small");
        }
        this.process(nonce, output, ByteBuffer.wrap(plaintext));
    }
    
    public byte[] decrypt(final byte[] nonce, final byte[] ciphertext) throws GeneralSecurityException {
        return this.decrypt(nonce, ByteBuffer.wrap(ciphertext));
    }
    
    public byte[] decrypt(final byte[] nonce, final ByteBuffer ciphertext) throws GeneralSecurityException {
        final ByteBuffer plaintext = ByteBuffer.allocate(ciphertext.remaining());
        this.process(nonce, plaintext, ciphertext);
        return plaintext.array();
    }
    
    private void process(final byte[] nonce, final ByteBuffer output, final ByteBuffer input) throws GeneralSecurityException {
        if (nonce.length != this.nonceSizeInBytes()) {
            throw new GeneralSecurityException("The nonce length (in bytes) must be " + this.nonceSizeInBytes());
        }
        final int length = input.remaining();
        for (int numBlocks = length / 64 + 1, i = 0; i < numBlocks; ++i) {
            final ByteBuffer keyStreamBlock = this.chacha20Block(nonce, i + this.initialCounter);
            if (i == numBlocks - 1) {
                Bytes.xor(output, input, keyStreamBlock, length % 64);
            }
            else {
                Bytes.xor(output, input, keyStreamBlock, 64);
            }
        }
    }
    
    ByteBuffer chacha20Block(final byte[] nonce, final int counter) {
        final int[] state = this.createInitialState(ChaCha20Util.toIntArray(nonce), counter);
        final int[] workingState = state.clone();
        ChaCha20Util.shuffleState(workingState);
        for (int i = 0; i < state.length; ++i) {
            final int[] array = state;
            final int n = i;
            array[n] += workingState[i];
        }
        final ByteBuffer out = ByteBuffer.allocate(64).order(ByteOrder.LITTLE_ENDIAN);
        out.asIntBuffer().put(state, 0, 16);
        return out;
    }
}
