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 Alexander Y. Kleymenov
     20 * @version $Revision$
     21 */
     22 
     23 package org.apache.harmony.crypto.tests.javax.crypto;
     24 
     25 import java.io.BufferedOutputStream;
     26 import java.io.ByteArrayOutputStream;
     27 import java.io.OutputStream;
     28 import java.security.AlgorithmParameters;
     29 import java.security.InvalidAlgorithmParameterException;
     30 import java.security.InvalidKeyException;
     31 import java.security.Key;
     32 import java.security.NoSuchAlgorithmException;
     33 import java.security.SecureRandom;
     34 import java.security.Security;
     35 import java.security.spec.AlgorithmParameterSpec;
     36 import java.util.Arrays;
     37 
     38 import javax.crypto.BadPaddingException;
     39 import javax.crypto.CipherSpi;
     40 import javax.crypto.IllegalBlockSizeException;
     41 import javax.crypto.KeyGenerator;
     42 import javax.crypto.NoSuchPaddingException;
     43 import javax.crypto.NullCipher;
     44 import javax.crypto.CipherOutputStream;
     45 import javax.crypto.Cipher;
     46 import javax.crypto.ShortBufferException;
     47 
     48 import junit.framework.TestCase;
     49 
     50 /**
     51  */
     52 public class CipherOutputStream1Test extends TestCase {
     53 
     54     private static class TestOutputStream extends ByteArrayOutputStream {
     55         private boolean closed = false;
     56 
     57         public void close() {
     58             closed = true;
     59         }
     60 
     61         public boolean wasClosed() {
     62             return closed;
     63         }
     64     }
     65 
     66     /**
     67      * CipherOutputStream(OutputStream os) method testing. Tests that
     68      * CipherOutputStream uses NullCipher if Cipher is not specified
     69      * in the constructor.
     70      */
     71     public void testCipherOutputStream() throws Exception {
     72         byte[] data = new byte[] { -127, -100, -50, -10, -1, 0, 1, 10, 50, 127 };
     73         TestOutputStream tos = new TestOutputStream();
     74         CipherOutputStream cos = new CipherOutputStream(tos){};
     75         cos.write(data);
     76         cos.flush();
     77         byte[] result = tos.toByteArray();
     78         if (!Arrays.equals(result, data)) {
     79             fail("NullCipher should be used " + "if Cipher is not specified.");
     80         }
     81     }
     82 
     83     /**
     84      * write(int b) method testing. Tests that method writes correct values to
     85      * the underlying output stream.
     86      */
     87     public void testWrite1() throws Exception {
     88         byte[] data = new byte[] { -127, -100, -50, -10, -1, 0, 1, 10, 50, 127 };
     89         TestOutputStream tos = new TestOutputStream();
     90         CipherOutputStream cos = new CipherOutputStream(tos, new NullCipher());
     91         for (int i = 0; i < data.length; i++) {
     92             cos.write(data[i]);
     93         }
     94         cos.flush();
     95         byte[] result = tos.toByteArray();
     96         if (!Arrays.equals(result, data)) {
     97             fail("CipherOutputStream wrote incorrect data.");
     98         }
     99     }
    100 
    101     /**
    102      * write(byte[] b) method testing. Tests that method writes correct values
    103      * to the underlying output stream.
    104      */
    105     public void testWrite2() throws Exception {
    106         byte[] data = new byte[] { -127, -100, -50, -10, -1, 0, 1, 10, 50, 127 };
    107         TestOutputStream tos = new TestOutputStream();
    108         CipherOutputStream cos = new CipherOutputStream(tos, new NullCipher());
    109         cos.write(data);
    110         cos.flush();
    111         byte[] result = tos.toByteArray();
    112         if (!Arrays.equals(result, data)) {
    113             fail("CipherOutputStream wrote incorrect data.");
    114         }
    115 
    116         try {
    117             cos.write(null);
    118             fail("NullPointerException expected");
    119         } catch (NullPointerException e) {
    120             //expected
    121         }
    122     }
    123 
    124     /**
    125      * write(byte[] b, int off, int len) method testing.
    126      */
    127     public void testWrite3() throws Exception {
    128         byte[] data = new byte[] { -127, -100, -50, -10, -1, 0, 1, 10, 50, 127 };
    129         TestOutputStream tos = new TestOutputStream();
    130         CipherOutputStream cos = new CipherOutputStream(tos, new NullCipher());
    131         for (int i = 0; i < data.length; i++) {
    132             cos.write(data, i, 1);
    133         }
    134         cos.flush();
    135         byte[] result = tos.toByteArray();
    136         if (!Arrays.equals(result, data)) {
    137             fail("CipherOutputStream wrote incorrect data.");
    138         }
    139     }
    140 
    141     /**
    142      * write(byte[] b, int off, int len)
    143      */
    144     public void testWrite4() throws Exception {
    145         //Regression for HARMONY-758
    146         try {
    147             new CipherOutputStream(new BufferedOutputStream((OutputStream) null), new NullCipher()).write(new byte[] {0}, 1, Integer.MAX_VALUE);
    148         } catch (IllegalArgumentException e) {
    149         }
    150     }
    151 
    152     /**
    153      * write(byte[] b, int off, int len)
    154      */
    155     public void testWrite5() throws Exception {
    156         //Regression for HARMONY-758
    157         Cipher cf = Cipher.getInstance("DES/CBC/PKCS5Padding");
    158         NullCipher nc = new NullCipher();
    159         CipherOutputStream stream1 = new CipherOutputStream(new BufferedOutputStream((OutputStream) null), nc);
    160         CipherOutputStream stream2 = new CipherOutputStream(stream1, cf);
    161         CipherOutputStream stream3 = new CipherOutputStream(stream2, nc);
    162         stream3.write(new byte[] {0}, 0, 0);
    163            //no exception expected
    164     }
    165 
    166     /**
    167      * flush() method testing. Tests that method flushes the data to the
    168      * underlying output stream.
    169      */
    170     public void testFlush() throws Exception {
    171         byte[] data = new byte[] { -127, -100, -50, -10, -1, 0, 1, 10, 50, 127 };
    172         TestOutputStream tos = new TestOutputStream();
    173         CipherOutputStream cos = new CipherOutputStream(tos){};
    174         cos.write(data);
    175         cos.flush();
    176         byte[] result = tos.toByteArray();
    177         if (!Arrays.equals(result, data)) {
    178             fail("CipherOutputStream did not flush the data.");
    179         }
    180     }
    181 
    182     /**
    183      * close() method testing. Tests that the method calls the close() method of
    184      * the underlying input stream.
    185      */
    186     public void testClose() throws Exception {
    187         byte[] data = new byte[] { -127, -100, -50, -10, -1, 0, 1, 10, 50, 127 };
    188         TestOutputStream tos = new TestOutputStream();
    189         CipherOutputStream cos = new CipherOutputStream(tos){};
    190         cos.write(data);
    191         cos.close();
    192         byte[] result = tos.toByteArray();
    193         if (!Arrays.equals(result, data)) {
    194             fail("CipherOutputStream did not flush the data.");
    195         }
    196         assertTrue("The close() method should call the close() method "
    197                 + "of its underlying output stream.", tos.wasClosed());
    198     }
    199 
    200     public void test_ConstructorLjava_io_OutputStreamLjavax_crypto_Cipher() throws
    201     NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException {
    202         ByteArrayOutputStream baos = new ByteArrayOutputStream();
    203 
    204         KeyGenerator kg = KeyGenerator.getInstance("DES");
    205         kg.init(56, new SecureRandom());
    206         Key key = kg.generateKey();
    207 
    208         Cipher c = Cipher.getInstance("DES/CBC/NoPadding");
    209         c.init(Cipher.ENCRYPT_MODE, key);
    210 
    211         CipherOutputStream cos = new CipherOutputStream(baos, c);
    212 
    213         assertNotNull(cos);
    214     }
    215 
    216     private static class CipherSpiThatThrowsOnSecondDoFinal extends CipherSpi {
    217 
    218         private boolean wasDoFinalCalled = false;
    219 
    220         @Override
    221         protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
    222 
    223         }
    224 
    225         @Override
    226         protected void engineSetPadding(String padding) throws NoSuchPaddingException {
    227 
    228         }
    229 
    230         @Override
    231         protected int engineGetBlockSize() {
    232             return 0;
    233         }
    234 
    235         @Override
    236         protected int engineGetOutputSize(int inputLen) {
    237             return 0;
    238         }
    239 
    240         @Override
    241         protected byte[] engineGetIV() {
    242             return new byte[0];
    243         }
    244 
    245         @Override
    246         protected AlgorithmParameters engineGetParameters() {
    247             return null;
    248         }
    249 
    250         @Override
    251         protected void engineInit(int opmode, Key key, SecureRandom random)
    252                 throws InvalidKeyException {
    253 
    254         }
    255 
    256         @Override
    257         protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params,
    258                 SecureRandom random)
    259                 throws InvalidKeyException, InvalidAlgorithmParameterException {
    260 
    261         }
    262 
    263         @Override
    264         protected void engineInit(int opmode, Key key, AlgorithmParameters params,
    265                 SecureRandom random)
    266                 throws InvalidKeyException, InvalidAlgorithmParameterException {
    267 
    268         }
    269 
    270         @Override
    271         protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) {
    272             return new byte[0];
    273         }
    274 
    275         @Override
    276         protected int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output,
    277                 int outputOffset) throws ShortBufferException {
    278             return 0;
    279         }
    280 
    281         @Override
    282         protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)
    283                 throws IllegalBlockSizeException, BadPaddingException {
    284             // Just call the other overriding for engineDoFinal.
    285             try {
    286                 engineDoFinal(input, inputOffset, inputLen, new byte[10], 0);
    287             } catch (ShortBufferException e) {
    288                 throw new RuntimeException(e);
    289             }
    290             return new byte[0];
    291         }
    292 
    293         @Override
    294         protected int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output,
    295                 int outputOffset)
    296                 throws ShortBufferException, IllegalBlockSizeException, BadPaddingException {
    297             if (wasDoFinalCalled) {
    298                 throw new UnsupportedOperationException(
    299                         "doFinal not supposed to be called two times");
    300             }
    301             wasDoFinalCalled = true;
    302             return 0;
    303         }
    304     };
    305 
    306 
    307     public void test_close_doubleCloseDoesntCallDoFinal() throws Exception {
    308         CipherSpi cipherSpiThatThrowsOnSecondDoFinal = new CipherSpiThatThrowsOnSecondDoFinal();
    309         Cipher cipherThatThrowsOnSecondDoFinal = new Cipher(
    310                 cipherSpiThatThrowsOnSecondDoFinal,
    311                 Security.getProviders()[0],
    312                 "SomeTransformation") {
    313         };
    314 
    315         TestOutputStream testOutputStream = new TestOutputStream();
    316         CipherOutputStream cipherOutputStream = new CipherOutputStream(
    317                 testOutputStream, cipherThatThrowsOnSecondDoFinal);
    318 
    319         cipherThatThrowsOnSecondDoFinal.init(Cipher.ENCRYPT_MODE, (Key) null);
    320 
    321         cipherOutputStream.close();
    322         // Should just check that it's already closed and return, without calling doFinal, thus
    323         // throwing any exception
    324         cipherOutputStream.close();
    325 
    326         // Check that the spi didn't change, as it might be changed dynamically by the Cipher
    327         // methods.
    328         assertEquals(cipherSpiThatThrowsOnSecondDoFinal,
    329                 cipherThatThrowsOnSecondDoFinal.getCurrentSpi());
    330     }
    331 }
    332 
    333