Home | History | Annotate | Download | only in crypto
      1 /*
      2  *  Licensed to the Apache Software Foundation (ASF) under one or more
      3  *  contributor license agreements.  See the NOTICE file distributed with
      4  *  this work for additional information regarding copyright ownership.
      5  *  The ASF licenses this file to You under the Apache License, Version 2.0
      6  *  (the "License"); you may not use this file except in compliance with
      7  *  the License.  You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  *  Unless required by applicable law or agreed to in writing, software
     12  *  distributed under the License is distributed on an "AS IS" BASIS,
     13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  *  See the License for the specific language governing permissions and
     15  *  limitations under the License.
     16  */
     17 
     18 /**
     19 * @author Vera Y. Petrashkova
     20 * @version $Revision$
     21 */
     22 
     23 package org.apache.harmony.crypto.tests.javax.crypto;
     24 
     25 import java.nio.DirectByteBuffer;
     26 import java.security.spec.AlgorithmParameterSpec;
     27 import java.security.InvalidAlgorithmParameterException;
     28 import java.security.InvalidKeyException;
     29 import java.security.Key;
     30 import java.security.NoSuchAlgorithmException;
     31 import java.security.SecureRandom;
     32 import java.security.AlgorithmParameters;
     33 import javax.crypto.BadPaddingException;
     34 import javax.crypto.IllegalBlockSizeException;
     35 import javax.crypto.NoSuchPaddingException;
     36 import javax.crypto.ShortBufferException;
     37 import javax.crypto.CipherSpi;
     38 import java.nio.ByteBuffer;
     39 import java.util.concurrent.atomic.AtomicInteger;
     40 
     41 import junit.framework.TestCase;
     42 
     43 
     44 /**
     45  * Tests for <code>CipherSpi</code> class constructors and methods.
     46  *
     47  */
     48 public class CipherSpiTest extends TestCase {
     49     class Mock_CipherSpi extends myCipherSpi {
     50 
     51         @Override
     52         protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)
     53                 throws IllegalBlockSizeException, BadPaddingException {
     54             return super.engineDoFinal(input, inputOffset, inputLen);
     55         }
     56 
     57         @Override
     58         protected int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output,
     59                 int outputOffset) throws ShortBufferException, IllegalBlockSizeException,
     60                 BadPaddingException {
     61             return super.engineDoFinal(input, inputOffset, inputLen, output, outputOffset);
     62         }
     63 
     64         @Override
     65         protected int engineGetBlockSize() {
     66             return super.engineGetBlockSize();
     67         }
     68 
     69         @Override
     70         protected byte[] engineGetIV() {
     71             return super.engineGetIV();
     72         }
     73 
     74         @Override
     75         protected int engineGetOutputSize(int inputLen) {
     76             return super.engineGetOutputSize(inputLen);
     77         }
     78 
     79         @Override
     80         protected AlgorithmParameters engineGetParameters() {
     81             return super.engineGetParameters();
     82         }
     83 
     84         @Override
     85         protected void engineInit(int opmode, Key key, SecureRandom random)
     86                 throws InvalidKeyException {
     87             super.engineInit(opmode, key, random);
     88         }
     89 
     90         @Override
     91         protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params,
     92                 SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException {
     93             super.engineInit(opmode, key, params, random);
     94         }
     95 
     96         @Override
     97         protected void engineInit(int opmode, Key key, AlgorithmParameters params,
     98                 SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException {
     99             super.engineInit(opmode, key, params, random);
    100         }
    101 
    102         @Override
    103         protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
    104             super.engineSetMode(mode);
    105         }
    106 
    107         @Override
    108         protected void engineSetPadding(String padding) throws NoSuchPaddingException {
    109             super.engineSetPadding(padding);
    110         }
    111 
    112         @Override
    113         protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) {
    114             return super.engineUpdate(input, inputOffset, inputLen);
    115         }
    116 
    117         @Override
    118         protected int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output,
    119                 int outputOffset) throws ShortBufferException {
    120             return super.engineUpdate(input, inputOffset, inputLen, output, outputOffset);
    121         }
    122 
    123         @Override
    124         protected int engineGetKeySize(Key key) throws InvalidKeyException {
    125             return super.engineGetKeySize(key);
    126         }
    127 
    128         @Override
    129         protected byte[] engineWrap(Key key) throws InvalidKeyException, IllegalBlockSizeException {
    130             return super.engineWrap(key);
    131         }
    132 
    133         @Override
    134         protected Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, int wrappedKeyType)
    135                 throws InvalidKeyException, NoSuchAlgorithmException {
    136             return super.engineUnwrap(wrappedKey, wrappedKeyAlgorithm, wrappedKeyType);
    137         }
    138     }
    139 
    140     /**
    141      * Test for <code>CipherSpi</code> constructor
    142      * Assertion: constructs CipherSpi
    143      */
    144     public void testCipherSpiTests01() throws IllegalBlockSizeException,
    145             BadPaddingException, ShortBufferException {
    146 
    147         Mock_CipherSpi cSpi = new Mock_CipherSpi();
    148         assertEquals("BlockSize is not 0", cSpi.engineGetBlockSize(), 0);
    149         assertEquals("OutputSize is not 0", cSpi.engineGetOutputSize(1), 0);
    150         byte[] bb = cSpi.engineGetIV();
    151         assertEquals("Length of result byte array is not 0", bb.length, 0);
    152         assertNull("Not null result", cSpi.engineGetParameters());
    153         byte[] bb1 = new byte[10];
    154         byte[] bb2 = new byte[10];
    155         bb = cSpi.engineUpdate(bb1, 1, 2);
    156         assertEquals("Incorrect result of engineUpdate(byte, int, int)",
    157                 bb.length, 2);
    158         bb = cSpi.engineDoFinal(bb1, 1, 2);
    159         assertEquals("Incorrect result of engineDoFinal(byte, int, int)", 2,
    160                 bb.length);
    161         assertEquals(
    162                 "Incorrect result of engineUpdate(byte, int, int, byte, int)",
    163                 cSpi.engineUpdate(bb1, 1, 2, bb2, 7), 2);
    164         assertEquals(
    165                 "Incorrect result of engineDoFinal(byte, int, int, byte, int)",
    166                 2, cSpi.engineDoFinal(bb1, 1, 2, bb2, 0));
    167     }
    168 
    169     /**
    170      * Test for <code>engineGetKeySize(Key)</code> method
    171      * Assertion: It throws UnsupportedOperationException if it is not overridden
    172      */
    173     public void testCipherSpi02() throws Exception {
    174         Mock_CipherSpi cSpi = new Mock_CipherSpi();
    175         try {
    176             cSpi.engineGetKeySize(null);
    177             fail("UnsupportedOperationException must be thrown");
    178         } catch (UnsupportedOperationException e) {
    179         }
    180     }
    181 
    182     /**
    183      * Test for <code>engineWrap(Key)</code> method
    184      * Assertion: It throws UnsupportedOperationException if it is not overridden
    185      */
    186     public void testCipherSpi03() throws Exception {
    187         Mock_CipherSpi cSpi = new Mock_CipherSpi();
    188         try {
    189             cSpi.engineWrap(null);
    190             fail("UnsupportedOperationException must be thrown");
    191         } catch (UnsupportedOperationException e) {
    192         }
    193     }
    194 
    195     /**
    196      * Test for <code>engineUnwrap(byte[], String, int)</code> method
    197      * Assertion: It throws UnsupportedOperationException if it is not overridden
    198      */
    199     public void testCipherSpi04() throws Exception {
    200         Mock_CipherSpi cSpi = new Mock_CipherSpi();
    201         try {
    202             cSpi.engineUnwrap(new byte[0], "", 0);
    203             fail("UnsupportedOperationException must be thrown");
    204         } catch (UnsupportedOperationException e) {
    205         }
    206     }
    207 
    208     /**
    209      * Test for <code>engineUpdate(ByteBuffer, ByteBuffer)</code> method
    210      * Assertions:
    211      * throws NullPointerException if one of these buffers is null;
    212      * throws ShortBufferException is there is no space in output to hold result
    213      */
    214     public void testCipherSpi05() throws ShortBufferException {
    215         Mock_CipherSpi cSpi = new Mock_CipherSpi();
    216         byte[] bb = { (byte) 0, (byte) 1, (byte) 2, (byte) 3, (byte) 4,
    217                 (byte) 5, (byte) 6, (byte) 7, (byte) 8, (byte) 9, (byte) 10 };
    218         int pos = 5;
    219         int len = bb.length;
    220         ByteBuffer bbNull = null;
    221         ByteBuffer bb1 = ByteBuffer.allocate(len);
    222         bb1.put(bb);
    223         bb1.position(0);
    224         try {
    225             cSpi.engineUpdate(bbNull, bb1);
    226             fail("NullPointerException must be thrown");
    227         } catch (NullPointerException e) {
    228         }
    229         try {
    230             cSpi.engineUpdate(bb1, bbNull);
    231             fail("NullPointerException must be thrown");
    232         } catch (NullPointerException e) {
    233         }
    234         ByteBuffer bb2 = ByteBuffer.allocate(bb.length);
    235         bb1.position(len);
    236         assertEquals("Incorrect number of stored bytes", 0, cSpi.engineUpdate(
    237                 bb1, bb2));
    238 
    239         bb1.position(0);
    240         bb2.position(len - 2);
    241         try {
    242             cSpi.engineUpdate(bb1, bb2);
    243             fail("ShortBufferException bust be thrown. Output buffer remaining: "
    244                     .concat(Integer.toString(bb2.remaining())));
    245         } catch (ShortBufferException e) {
    246         }
    247         bb1.position(10);
    248         bb2.position(0);
    249         assertTrue("Incorrect number of stored bytes", cSpi.engineUpdate(bb1,
    250                 bb2) > 0);
    251         bb1.position(bb.length);
    252         cSpi.engineUpdate(bb1, bb2);
    253 
    254         bb1.position(pos);
    255         bb2.position(0);
    256         int res = cSpi.engineUpdate(bb1, bb2);
    257         assertTrue("Incorrect result", res > 0);
    258     }
    259 
    260     /**
    261      * Test for <code>engineDoFinal(ByteBuffer, ByteBuffer)</code> method
    262      * Assertions:
    263      * throws NullPointerException if one of these buffers is null;
    264      * throws ShortBufferException is there is no space in output to hold result
    265      */
    266     public void testCipherSpi06() throws BadPaddingException,
    267             ShortBufferException, IllegalBlockSizeException {
    268         Mock_CipherSpi cSpi = new Mock_CipherSpi();
    269         int len = 10;
    270         byte[] bbuf = new byte[len];
    271         for (int i = 0; i < bbuf.length; i++) {
    272             bbuf[i] = (byte) i;
    273         }
    274         ByteBuffer bb1 = ByteBuffer.wrap(bbuf);
    275         ByteBuffer bbNull = null;
    276         try {
    277             cSpi.engineDoFinal(bbNull, bb1);
    278             fail("NullPointerException must be thrown");
    279         } catch (NullPointerException e) {
    280         }
    281         try {
    282             cSpi.engineDoFinal(bb1, bbNull);
    283             fail("NullPointerException must be thrown");
    284         } catch (NullPointerException e) {
    285         }
    286         ByteBuffer bb2 = ByteBuffer.allocate(len);
    287         bb1.position(bb1.limit());
    288         assertEquals("Incorrect result", 0, cSpi.engineDoFinal(bb1, bb2));
    289 
    290         bb1.position(0);
    291         bb2.position(len - 2);
    292         try {
    293             cSpi.engineDoFinal(bb1, bb2);
    294             fail("ShortBufferException must be thrown. Output buffer remaining: "
    295                     .concat(Integer.toString(bb2.remaining())));
    296         } catch (ShortBufferException e) {
    297         }
    298         int pos = 5;
    299         bb1.position(pos);
    300         bb2.position(0);
    301         assertTrue("Incorrect result", cSpi.engineDoFinal(bb1, bb2) > 0);
    302     }
    303 
    304     public void testCrypt_doNotCallPositionInNonArrayBackedInputBuffer() throws Exception {
    305         ByteBuffer nonArrayBackedInputBuffer = new MockNonArrayBackedByteBuffer(10, false);
    306         ByteBuffer nonArrayBackedOutputBuffer = new MockNonArrayBackedByteBuffer(10, false);
    307         Mock_CipherSpi cipherSpi = new Mock_CipherSpi() {
    308             public int engineGetOutputSize(int inputLength) {
    309                 return inputLength;
    310             }
    311         };
    312         cipherSpi.engineUpdate(nonArrayBackedInputBuffer, nonArrayBackedOutputBuffer);
    313         assertEquals(0, nonArrayBackedInputBuffer.position());
    314     }
    315 
    316     public void testCrypt_doNotCallPutForZeroLengthOutput() throws Exception {
    317         ByteBuffer nonArrayBackedInputBuffer = new MockNonArrayBackedByteBuffer(10, false);
    318         ByteBuffer nonArrayBackedOutputBuffer = new MockNonArrayBackedByteBuffer(10, false) {
    319             @Override
    320             public ByteBuffer put(byte[] dst, int offset, int length) {
    321                 if (length == 0) {
    322                     throw new IllegalStateException("put shouldn't be called with length 0");
    323                 }
    324                 return this;
    325             }
    326         };
    327 
    328         Mock_CipherSpi cipherSpi = new Mock_CipherSpi() {
    329             public int engineUpdate(
    330                     byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset) {
    331                 return 0;
    332             }
    333         };
    334 
    335         // The put method is not called in the output buffer and so the test passes.
    336         cipherSpi.engineUpdate(nonArrayBackedInputBuffer, nonArrayBackedOutputBuffer);
    337     }
    338 
    339     // In case a call to engineGetOutputSize returns 0 for the whole input size, but a positive
    340     // value for the chunk size to be written, check that the positive output size is used in the
    341     // second attempt to read from the the buffer.
    342     public void testCrypt_outputSizeUpdatedAfterShortBufferException()
    343             throws Exception {
    344 
    345         // 4096 is the value hardcoded for a maximum array allocation in CipherSpi#getTempArraySize
    346         final int maxInternalArrayAllocation = 4096;
    347         // The length of the input is greater than the max chunk allowed, so the size of the chunk
    348         // and the size of the input will differ.
    349         final int testInputLength = maxInternalArrayAllocation + 1;
    350         // Length to be returned the second time engineGetOutputSize is called (that is, when it's
    351         // called with maxInternalArrayAllocation). First length returned (that is, when it's
    352         // called with testInputLength) is 0.
    353         final int testSecondOutputLength = 1000;
    354 
    355         final AtomicInteger firstGetLength = new AtomicInteger(0);
    356         final AtomicInteger secondGetLength = new AtomicInteger(0);
    357 
    358         ByteBuffer inputBuffer = new MockNonArrayBackedByteBuffer(testInputLength, false) {
    359             private boolean getWasCalled = false;
    360 
    361             @Override
    362             public ByteBuffer get(byte[] dst, int offset, int length) {
    363                 if (!getWasCalled) {
    364                     getWasCalled = true;
    365                     firstGetLength.set(length);
    366                 } else {
    367                     if (secondGetLength.get() == 0) {
    368                         secondGetLength.set(length);
    369                     }
    370                 }
    371                 return this;
    372             }
    373         };
    374 
    375         ByteBuffer outputBuffer = new MockNonArrayBackedByteBuffer(10, false);
    376 
    377         Mock_CipherSpi cipherSpi = new Mock_CipherSpi() {
    378             @Override
    379             public int engineGetOutputSize(int inputLength) {
    380                 if (inputLength == testInputLength) {
    381                     return 0;
    382                 } else if (inputLength == maxInternalArrayAllocation) {
    383                     return testSecondOutputLength;
    384                 } else {
    385                     throw new IllegalStateException("Unexpected value " + inputLength);
    386                 }
    387             }
    388 
    389             @Override
    390             public int engineUpdate(
    391                     byte[] inArray, int inOfs, int inLen, byte[] outArray, int outputOffset)
    392                     throws ShortBufferException {
    393                 if (inLen == maxInternalArrayAllocation) {
    394                     throw new ShortBufferException("to be caught in order to retry with a new"
    395                             + "output size");
    396                 }
    397                 return 0;
    398             }
    399         };
    400 
    401         cipherSpi.engineUpdate(inputBuffer, outputBuffer);
    402 
    403         assertEquals(
    404                 "first call to get must use the input length, as the output length "
    405                         + "from engineGetOutputSize is 0",
    406                 maxInternalArrayAllocation,
    407                 firstGetLength.get());
    408 
    409         assertEquals(
    410                 "second call to get must use the new output length",
    411                 testSecondOutputLength,
    412                 secondGetLength.get());
    413     }
    414 
    415     // The tests using ByteBuffer depend on final methods (like hasArray) that cannot be mocked in
    416     // Mockito, so the mock is done manually. ByteBuffer has abstract methods that are
    417     // package-private, so extending DirectByteBuffer. It happens to be not backed by an array, so
    418     // we use it when we need a byte buffer not array-backed.
    419     private class MockNonArrayBackedByteBuffer extends DirectByteBuffer {
    420         public MockNonArrayBackedByteBuffer(int capacity, boolean isReadOnly) {
    421             super(capacity, 0 /* addr */, null /* fd */, null /* unmapper */, isReadOnly);
    422         }
    423 
    424         @Override
    425         public ByteBuffer get(byte[] dst, int offset, int length) {
    426             return this;
    427         }
    428 
    429         @Override
    430         public ByteBuffer put(byte[] dst, int offset, int length) {
    431             return this;
    432         }
    433     }
    434 }
    435 /**
    436  *
    437  * Additional class for CipherGeneratorSpi constructor verification
    438  */
    439 
    440 class myCipherSpi extends CipherSpi {
    441     private byte[] initV;
    442 
    443     private static byte[] resV = { (byte) 7, (byte) 6, (byte) 5, (byte) 4,
    444             (byte) 3, (byte) 2, (byte) 1, (byte) 0 };
    445 
    446     public myCipherSpi() {
    447         this.initV = new byte[0];
    448     }
    449 
    450     protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
    451     }
    452 
    453     protected void engineSetPadding(String padding)
    454             throws NoSuchPaddingException {
    455     }
    456 
    457     protected int engineGetBlockSize() {
    458         return 0;
    459     }
    460 
    461     protected int engineGetOutputSize(int inputLen) {
    462         return 0;
    463     }
    464 
    465     protected byte[] engineGetIV() {
    466         return new byte[0];
    467     }
    468 
    469     protected AlgorithmParameters engineGetParameters() {
    470         return null;
    471     }
    472 
    473     protected void engineInit(int opmode, Key key, SecureRandom random)
    474             throws InvalidKeyException {
    475     }
    476 
    477     protected void engineInit(int opmode, Key key,
    478             AlgorithmParameterSpec params, SecureRandom random)
    479             throws InvalidKeyException, InvalidAlgorithmParameterException {
    480     }
    481 
    482     protected void engineInit(int opmode, Key key, AlgorithmParameters params,
    483             SecureRandom random) throws InvalidKeyException,
    484             InvalidAlgorithmParameterException {
    485     }
    486 
    487     protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) {
    488         if (initV.length < inputLen) {
    489             initV = new byte[inputLen];
    490         }
    491         for (int i = 0; i < inputLen; i++) {
    492             initV[i] = input[inputOffset + i];
    493         }
    494         return initV;
    495     }
    496 
    497     protected int engineUpdate(byte[] input, int inputOffset, int inputLen,
    498             byte[] output, int outputOffset) throws ShortBufferException {
    499         byte []res = engineUpdate(input, inputOffset, inputLen);
    500         int t = res.length;
    501         if ((output.length - outputOffset) < t) {
    502             throw new ShortBufferException("Update");
    503         }
    504         for (int i = 0; i < t; i++) {
    505             output[i + outputOffset] = initV[i];
    506         }
    507         return t;
    508     }
    509 
    510     protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)
    511             throws IllegalBlockSizeException, BadPaddingException {
    512         if (resV.length > inputLen) {
    513             byte[] bb = new byte[inputLen];
    514             for (int i = 0; i < inputLen; i++) {
    515                 bb[i] = resV[i];
    516             }
    517             return bb;
    518         }
    519         return resV;
    520     }
    521 
    522     protected int engineDoFinal(byte[] input, int inputOffset, int inputLen,
    523             byte[] output, int outputOffset) throws ShortBufferException,
    524             IllegalBlockSizeException, BadPaddingException {
    525         byte[] res = engineDoFinal(input, inputOffset, inputLen);
    526 
    527         int t = res.length;
    528         if ((output.length - outputOffset) < t) {
    529             throw new ShortBufferException("DoFinal");
    530         }
    531         for (int i = 0; i < t; i++) {
    532             output[i + outputOffset] = res[i];
    533         }
    534         return t;
    535     }
    536 
    537 
    538     protected int engineUpdate(ByteBuffer input, ByteBuffer output)
    539     throws ShortBufferException {
    540         return super.engineUpdate(input, output);
    541     }
    542     protected int engineDoFinal(ByteBuffer input, ByteBuffer output)
    543     throws ShortBufferException, IllegalBlockSizeException,
    544     BadPaddingException {
    545         return super.engineDoFinal(input, output);
    546     }
    547 }
    548