/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

/**
* @author Vera Y. Petrashkova
* @version $Revision$
*/

package org.apache.harmony.crypto.tests.javax.crypto;

import dalvik.annotation.TestTargetClass;
import dalvik.annotation.TestTargets;
import dalvik.annotation.TestLevel;
import dalvik.annotation.TestTargetNew;

import java.security.spec.AlgorithmParameterSpec;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.AlgorithmParameters;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.ShortBufferException;
import javax.crypto.CipherSpi;
import java.nio.ByteBuffer;

import junit.framework.TestCase;


@TestTargetClass(CipherSpi.class)
/**
 * Tests for <code>CipherSpi</code> class constructors and methods.
 * 
 */
public class CipherSpiTest extends TestCase {
    class Mock_CipherSpi extends myCipherSpi {

        @Override
        protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)
                throws IllegalBlockSizeException, BadPaddingException {
            return super.engineDoFinal(input, inputOffset, inputLen);
        }

        @Override
        protected int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output,
                int outputOffset) throws ShortBufferException, IllegalBlockSizeException,
                BadPaddingException {
            return super.engineDoFinal(input, inputOffset, inputLen, output, outputOffset);
        }

        @Override
        protected int engineGetBlockSize() {
            return super.engineGetBlockSize();
        }

        @Override
        protected byte[] engineGetIV() {
            return super.engineGetIV();
        }

        @Override
        protected int engineGetOutputSize(int inputLen) {
            return super.engineGetOutputSize(inputLen);
        }

        @Override
        protected AlgorithmParameters engineGetParameters() {
            return super.engineGetParameters();
        }

        @Override
        protected void engineInit(int opmode, Key key, SecureRandom random)
                throws InvalidKeyException {
            super.engineInit(opmode, key, random);
        }

        @Override
        protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params,
                SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException {
            super.engineInit(opmode, key, params, random);
        }

        @Override
        protected void engineInit(int opmode, Key key, AlgorithmParameters params,
                SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException {
            super.engineInit(opmode, key, params, random);
        }

        @Override
        protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
            super.engineSetMode(mode);
        }

        @Override
        protected void engineSetPadding(String padding) throws NoSuchPaddingException {
            super.engineSetPadding(padding);
        }

        @Override
        protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) {
            return super.engineUpdate(input, inputOffset, inputLen);
        }

        @Override
        protected int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output,
                int outputOffset) throws ShortBufferException {
            return super.engineUpdate(input, inputOffset, inputLen, output, outputOffset);
        }

        @Override
        protected int engineGetKeySize(Key key) throws InvalidKeyException {
            return super.engineGetKeySize(key);
        }

        @Override
        protected byte[] engineWrap(Key key) throws InvalidKeyException, IllegalBlockSizeException {
            return super.engineWrap(key);
        }

        @Override
        protected Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, int wrappedKeyType)
                throws InvalidKeyException, NoSuchAlgorithmException {
            return super.engineUnwrap(wrappedKey, wrappedKeyAlgorithm, wrappedKeyType);
        }
    }

    /**
     * Test for <code>CipherSpi</code> constructor 
     * Assertion: constructs CipherSpi
     */
    @TestTargetNew(
        level = TestLevel.COMPLETE,
        notes = "",
        method = "CipherSpi",
        args = {}
    )
    public void testCipherSpiTests01() throws IllegalBlockSizeException,
            BadPaddingException, ShortBufferException {
        
        Mock_CipherSpi cSpi = new Mock_CipherSpi();
        assertEquals("BlockSize is not 0", cSpi.engineGetBlockSize(), 0);
        assertEquals("OutputSize is not 0", cSpi.engineGetOutputSize(1), 0);
        byte[] bb = cSpi.engineGetIV();
        assertEquals("Length of result byte array is not 0", bb.length, 0);
        assertNull("Not null result", cSpi.engineGetParameters());
        byte[] bb1 = new byte[10];
        byte[] bb2 = new byte[10];
        bb = cSpi.engineUpdate(bb1, 1, 2);
        assertEquals("Incorrect result of engineUpdate(byte, int, int)",
                bb.length, 2);
        bb = cSpi.engineDoFinal(bb1, 1, 2);
        assertEquals("Incorrect result of engineDoFinal(byte, int, int)", 2,
                bb.length);
        assertEquals(
                "Incorrect result of engineUpdate(byte, int, int, byte, int)",
                cSpi.engineUpdate(bb1, 1, 2, bb2, 7), 2);
        assertEquals(
                "Incorrect result of engineDoFinal(byte, int, int, byte, int)",
                2, cSpi.engineDoFinal(bb1, 1, 2, bb2, 0));
    }
    
    /**
     * Test for <code>engineGetKeySize(Key)</code> method 
     * Assertion: It throws UnsupportedOperationException if it is not overridden
     */
    @TestTargetNew(
        level = TestLevel.COMPLETE,
        method = "engineGetKeySize",
        args = {java.security.Key.class}
    )
    public void testCipherSpi02() throws Exception {
        Mock_CipherSpi cSpi = new Mock_CipherSpi();
        try {
            cSpi.engineGetKeySize(null);
            fail("UnsupportedOperationException must be thrown");
        } catch (UnsupportedOperationException e) {
        }
    }

    /**
     * Test for <code>engineWrap(Key)</code> method 
     * Assertion: It throws UnsupportedOperationException if it is not overridden
     */
    @TestTargetNew(
        level = TestLevel.PARTIAL,
        notes = "Functionality not tested.",
        method = "engineWrap",
        args = {java.security.Key.class}
    )
    public void testCipherSpi03() throws Exception {
        Mock_CipherSpi cSpi = new Mock_CipherSpi();
        try {
            cSpi.engineWrap(null);
            fail("UnsupportedOperationException must be thrown");
        } catch (UnsupportedOperationException e) {
        }
    }

    /**
     * Test for <code>engineUnwrap(byte[], String, int)</code> method
     * Assertion: It throws UnsupportedOperationException if it is not overridden
     */
    @TestTargetNew(
        level = TestLevel.PARTIAL,
        notes = "Functionality not tested.",
        method = "engineUnwrap",
        args = {byte[].class, java.lang.String.class, int.class}
    )
    public void testCipherSpi04() throws Exception {
        Mock_CipherSpi cSpi = new Mock_CipherSpi();
        try {
            cSpi.engineUnwrap(new byte[0], "", 0);
            fail("UnsupportedOperationException must be thrown");
        } catch (UnsupportedOperationException e) {
        }
    }
    
    /**
     * Test for <code>engineUpdate(ByteBuffer, ByteBuffer)</code> method
     * Assertions:
     * throws NullPointerException if one of these buffers is null;
     * throws ShortBufferException is there is no space in output to hold result
     */
    @TestTargetNew(
        level = TestLevel.COMPLETE,
        notes = "",
        method = "engineUpdate",
        args = {java.nio.ByteBuffer.class, java.nio.ByteBuffer.class}
    )
    public void testCipherSpi05() throws ShortBufferException {
        Mock_CipherSpi cSpi = new Mock_CipherSpi();
        byte[] bb = { (byte) 0, (byte) 1, (byte) 2, (byte) 3, (byte) 4,
                (byte) 5, (byte) 6, (byte) 7, (byte) 8, (byte) 9, (byte) 10 };
        int pos = 5;
        int len = bb.length;
        ByteBuffer bbNull = null;
        ByteBuffer bb1 = ByteBuffer.allocate(len);
        bb1.put(bb);
        bb1.position(0);
        try {
            cSpi.engineUpdate(bbNull, bb1);
            fail("NullPointerException must be thrown");
        } catch (NullPointerException e) {
        }
        try {
            cSpi.engineUpdate(bb1, bbNull);
            fail("NullPointerException must be thrown");
        } catch (NullPointerException e) {
        }
        ByteBuffer bb2 = ByteBuffer.allocate(bb.length);
        bb1.position(len);
        assertEquals("Incorrect number of stored bytes", 0, cSpi.engineUpdate(
                bb1, bb2));

        bb1.position(0);
        bb2.position(len - 2);
        try {
            cSpi.engineUpdate(bb1, bb2);
            fail("ShortBufferException bust be thrown. Output buffer remaining: "
                    .concat(Integer.toString(bb2.remaining())));
        } catch (ShortBufferException e) {
        }
        bb1.position(10);
        bb2.position(0);
        assertTrue("Incorrect number of stored bytes", cSpi.engineUpdate(bb1,
                bb2) > 0);
        bb1.position(bb.length);
        cSpi.engineUpdate(bb1, bb2);

        bb1.position(pos);
        bb2.position(0);
        int res = cSpi.engineUpdate(bb1, bb2);
        assertTrue("Incorrect result", res > 0);
    }

    /**
     * Test for <code>engineDoFinal(ByteBuffer, ByteBuffer)</code> method
     * Assertions: 
     * throws NullPointerException if one of these buffers is null;
     * throws ShortBufferException is there is no space in output to hold result
     */
    @TestTargetNew(
        level = TestLevel.PARTIAL,
        notes = "BadPaddingException & IllegalBlockSizeException checking missed.",
        method = "engineDoFinal",
        args = {java.nio.ByteBuffer.class, java.nio.ByteBuffer.class}
    )
    public void testCipherSpi06() throws BadPaddingException,
            ShortBufferException, IllegalBlockSizeException {
        Mock_CipherSpi cSpi = new Mock_CipherSpi();
        int len = 10;
        byte[] bbuf = new byte[len];
        for (int i = 0; i < bbuf.length; i++) {
            bbuf[i] = (byte) i;
        }
        ByteBuffer bb1 = ByteBuffer.wrap(bbuf);
        ByteBuffer bbNull = null;
        try {
            cSpi.engineDoFinal(bbNull, bb1);
            fail("NullPointerException must be thrown");
        } catch (NullPointerException e) {
        }
        try {
            cSpi.engineDoFinal(bb1, bbNull);
            fail("NullPointerException must be thrown");
        } catch (NullPointerException e) {
        }
        ByteBuffer bb2 = ByteBuffer.allocate(len);
        bb1.position(bb1.limit());
        assertEquals("Incorrect result", 0, cSpi.engineDoFinal(bb1, bb2));

        bb1.position(0);
        bb2.position(len - 2);
        try {
            cSpi.engineDoFinal(bb1, bb2);
            fail("ShortBufferException must be thrown. Output buffer remaining: "
                    .concat(Integer.toString(bb2.remaining())));
        } catch (ShortBufferException e) {
        }
        int pos = 5;
        bb1.position(pos);
        bb2.position(0);
        assertTrue("Incorrect result", cSpi.engineDoFinal(bb1, bb2) > 0);
    }
}
/**
 * 
 * Additional class for CipherGeneratorSpi constructor verification
 */

class myCipherSpi extends CipherSpi {
    private byte[] initV;

    private static byte[] resV = { (byte) 7, (byte) 6, (byte) 5, (byte) 4,
            (byte) 3, (byte) 2, (byte) 1, (byte) 0 };

    public myCipherSpi() {
        this.initV = new byte[0];
    }

    protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
    }

    protected void engineSetPadding(String padding)
            throws NoSuchPaddingException {
    }

    protected int engineGetBlockSize() {
        return 0;
    }

    protected int engineGetOutputSize(int inputLen) {
        return 0;
    }

    protected byte[] engineGetIV() {
        return new byte[0];
    }

    protected AlgorithmParameters engineGetParameters() {
        return null;
    }

    protected void engineInit(int opmode, Key key, SecureRandom random)
            throws InvalidKeyException {
    }

    protected void engineInit(int opmode, Key key,
            AlgorithmParameterSpec params, SecureRandom random)
            throws InvalidKeyException, InvalidAlgorithmParameterException {
    }

    protected void engineInit(int opmode, Key key, AlgorithmParameters params,
            SecureRandom random) throws InvalidKeyException,
            InvalidAlgorithmParameterException {
    }

    protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) {
        if (initV.length < inputLen) {
            initV = new byte[inputLen];
        }
        for (int i = 0; i < inputLen; i++) {
            initV[i] = input[inputOffset + i];
        }
        return initV;
    }

    protected int engineUpdate(byte[] input, int inputOffset, int inputLen,
            byte[] output, int outputOffset) throws ShortBufferException {
        byte []res = engineUpdate(input, inputOffset, inputLen);
        int t = res.length;
        if ((output.length - outputOffset) < t) {
            throw new ShortBufferException("Update");
        }
        for (int i = 0; i < t; i++) {
            output[i + outputOffset] = initV[i];
        }
        return t;
    }

    protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)
            throws IllegalBlockSizeException, BadPaddingException {
        if (resV.length > inputLen) {
            byte[] bb = new byte[inputLen];
            for (int i = 0; i < inputLen; i++) {
                bb[i] = resV[i];
            }
            return bb;
        }
        return resV;
    }

    protected int engineDoFinal(byte[] input, int inputOffset, int inputLen,
            byte[] output, int outputOffset) throws ShortBufferException,
            IllegalBlockSizeException, BadPaddingException {
        byte[] res = engineDoFinal(input, inputOffset, inputLen);
        
        int t = res.length;
        if ((output.length - outputOffset) < t) {
            throw new ShortBufferException("DoFinal");
        }
        for (int i = 0; i < t; i++) {            
            output[i + outputOffset] = res[i];
        }
        return t;
    }

    
    protected int engineUpdate(ByteBuffer input, ByteBuffer output)
    throws ShortBufferException {
        return super.engineUpdate(input, output);
    }
    protected int engineDoFinal(ByteBuffer input, ByteBuffer output)
    throws ShortBufferException, IllegalBlockSizeException,
    BadPaddingException {
        return super.engineDoFinal(input, output);
    }
}
