package at.archistar.crypto.symmetric;

import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Security;

import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.engines.ChaChaEngine;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

/**
 * basic ChaCha20-based encryption
 *
 * TODO: we are currently using a fixed IV!
 */
public class ChaCha20Encryptor implements Encryptor {

    private final byte randomIvBytes[] = {0, 1, 2, 3, 4, 5, 6, 7};

    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    @Override
    public byte[] encrypt(byte[] data, byte[] randomKeyBytes) throws IOException, InvalidKeyException,
            InvalidAlgorithmParameterException, InvalidCipherTextException {

        ChaChaEngine cipher = new ChaChaEngine();
        cipher.init(true, new ParametersWithIV(new KeyParameter(randomKeyBytes), randomIvBytes));

        byte[] result = new byte[data.length];
        cipher.processBytes(data, 0, data.length, result, 0);
        return result;
    }

    @Override
    public byte[] decrypt(byte[] data, byte[] randomKeyBytes)
            throws InvalidKeyException, InvalidAlgorithmParameterException, IOException,
            IllegalStateException, InvalidCipherTextException {

        ChaChaEngine cipher = new ChaChaEngine();
        cipher.init(false, new ParametersWithIV(new KeyParameter(randomKeyBytes), randomIvBytes));

        byte[] result = new byte[data.length];
        cipher.processBytes(data, 0, data.length, result, 0);
        return result;
    }

    /**
     * Special method to decrypt partial data
     *
     * @param data to decrypt
     * @param randomKeyBytes key to use
     * @param startingByte the starting position within the (complete) data
     * @return decrypted data
     */
    public byte[] decrypt(byte[] data, byte[] randomKeyBytes, long startingByte) {

        ChaChaEngine cipher = new ChaChaEngine();
        cipher.init(false, new ParametersWithIV(new KeyParameter(randomKeyBytes), randomIvBytes));
        cipher.skip(startingByte);
        byte[] result = new byte[data.length];
        cipher.processBytes(data, 0, data.length, result, 0);
        return result;
    }

    @Override
    public int getKeyLength() {
        return 32;
    }

    @Override
    public String toString() {
        return "ChaCha20()";
    }
}