Package org.syncany.tests.crypto

Source Code of org.syncany.tests.crypto.AesGcmWithBcInputStreamTest

/*
* Syncany, www.syncany.org
* Copyright (C) 2011-2014 Philipp C. Heckel <philipp.heckel@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
package org.syncany.tests.crypto;

import static org.junit.Assert.*;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.Security;

import javax.crypto.Cipher;
import javax.crypto.CipherOutputStream;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.io.InvalidCipherTextIOException;
import org.bouncycastle.crypto.modes.AEADBlockCipher;
import org.bouncycastle.crypto.modes.GCMBlockCipher;
import org.bouncycastle.crypto.params.AEADParameters;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.Arrays;
import org.junit.Test;
import org.syncany.config.Logging;

/**
* Tests to test the Bouncy Castle implementation of the CipherInputStream
*
* @see https://github.com/binwiederhier/cipherinputstream-aes-gcm/blob/e9759ca71557e5d1da26ae72f6ce5aac918e34b0/src/CipherInputStreamIssuesTests.java
* @see http://blog.philippheckel.com/2014/03/01/cipherinputstream-for-aead-modes-is-broken-in-jdk7-gcm/
*/
public class AesGcmWithBcInputStreamTest {
  private static final SecureRandom secureRandom = new SecureRandom();

  static {
    Logging.init();
    Security.addProvider(new BouncyCastleProvider());
  }
 
  @Test
  public void testD_BouncyCastleCipherInputStreamWithAesGcm() throws InvalidKeyException, InvalidAlgorithmParameterException, IOException, NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException {
    // Encrypt (not interesting in this example)
    byte[] randomKey = createRandomArray(16);
    byte[] randomIv = createRandomArray(16);   
    byte[] originalPlaintext = "Confirm 100$ pay".getBytes("ASCII");  
    byte[] originalCiphertext = encryptWithAesGcm(originalPlaintext, randomKey, randomIv);
   
    // Attack / alter ciphertext (an attacker would do this!)
    byte[] alteredCiphertext = Arrays.clone(originalCiphertext);   
    alteredCiphertext[8] = (byte) (alteredCiphertext[8] ^ 0x08); // <<< Change 100$ to 900$
   
    // Decrypt with BouncyCastle implementation of CipherInputStream
    AEADBlockCipher cipher = new GCMBlockCipher(new AESEngine());
    cipher.init(false, new AEADParameters(new KeyParameter(randomKey), 128, randomIv));
   
    try {
      readFromStream(new org.bouncycastle.crypto.io.CipherInputStream(new ByteArrayInputStream(alteredCiphertext), cipher));
      //             ^^^^^^^^^^^^^^^ INTERESTING PART ^^^^^^^^^^^^^^^^ 
      //
      //  The BouncyCastle implementation of the CipherInputStream detects MAC verification errors and
      //  throws a InvalidCipherTextIOException if an error occurs. Nice! A more or less minor issue
      //  however is that it is incompatible with the standard JCE Cipher class from the javax.crypto
      //  package. The new interface AEADBlockCipher must be used. The code below is not executed.   

      fail("Test D: org.bouncycastle.crypto.io.CipherInputStream:        NOT OK, tampering not detected");           
    }
    catch (InvalidCipherTextIOException e) {
      System.out.println("Test D: org.bouncycastle.crypto.io.CipherInputStream:        OK, tampering detected");           
    }
  }
 
  @Test
  public void testE_BouncyCastleCipherInputStreamWithAesGcmLongPlaintext() throws InvalidKeyException, InvalidAlgorithmParameterException, IOException, NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException {
    // Encrypt (not interesting in this example)
    byte[] randomKey = createRandomArray(16);
    byte[] randomIv = createRandomArray(16);   
    byte[] originalPlaintext = createRandomArray(4080); // <<<< 4080 bytes fails, 4079 bytes works!  
    byte[] originalCiphertext = encryptWithAesGcm(originalPlaintext, randomKey, randomIv);
   
    // Decrypt with BouncyCastle implementation of CipherInputStream
    AEADBlockCipher cipher = new GCMBlockCipher(new AESEngine());
    cipher.init(false, new AEADParameters(new KeyParameter(randomKey), 128, randomIv));
   
    try {
      readFromStream(new org.bouncycastle.crypto.io.CipherInputStream(new ByteArrayInputStream(originalCiphertext), cipher));
      //             ^^^^^^^^^^^^^^^ INTERESTING PART ^^^^^^^^^^^^^^^^ 
      //
      //  In this example, the BouncyCastle implementation of the CipherInputStream throws an ArrayIndexOutOfBoundsException.
      //  The only difference to the example above is that the plaintext is now 4080 bytes long! For 4079 bytes plaintexts,
      //  everything works just fine.

      System.out.println("Test E: org.bouncycastle.crypto.io.CipherInputStream:        OK, throws no exception");           
    }
    catch (IOException e) {
      fail("Test E: org.bouncycastle.crypto.io.CipherInputStream:        NOT OK throws: "+e.getMessage());
    }
  } 
 
  private static byte[] readFromStream(InputStream inputStream) throws IOException {
    ByteArrayOutputStream decryptedPlaintextOutputStream = new ByteArrayOutputStream();
   
    int read = -1;
    byte[] buffer = new byte[16];
   
    while (-1 != (read = inputStream.read(buffer))) {
      decryptedPlaintextOutputStream.write(buffer, 0, read);
    }
   
    inputStream.close();
    decryptedPlaintextOutputStream.close();
   
    return decryptedPlaintextOutputStream.toByteArray();     
  }
 
  private static byte[] encryptWithAesGcm(byte[] plaintext, byte[] randomKeyBytes, byte[] randomIvBytes) throws IOException, InvalidKeyException,
      InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException {

    SecretKey randomKey = new SecretKeySpec(randomKeyBytes, "AES");
   
    Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "BC");
    cipher.init(Cipher.ENCRYPT_MODE, randomKey, new IvParameterSpec(randomIvBytes));
   
    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    CipherOutputStream cipherOutputStream = new CipherOutputStream(byteArrayOutputStream, cipher);
   
    cipherOutputStream.write(plaintext);
    cipherOutputStream.close();
   
    return byteArrayOutputStream.toByteArray();
  }
 
  private static byte[] createRandomArray(int size) {
    byte[] randomByteArray = new byte[size];
    secureRandom.nextBytes(randomByteArray);

    return randomByteArray;
  }
}
TOP

Related Classes of org.syncany.tests.crypto.AesGcmWithBcInputStreamTest

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.