AES + HMAC encryption on multiple streams - Java

I am developing a small program to encrypt / decrypt a binary with AES-256 and HMAC to check the results.

My code is based on the Java implementation of AESCrypt, but I wanted to modify it to allow multiple threads to do work at the same time.

I get the size of the original bytes and calculate the number of blocks from 16 bytes per stream, then start streams with offset information that is applied to read and write (because there is a header for the encrypted file, so offset_write = offset_read + header_length).

When it finished encrypting, I passed the output content (no header) through HMAC to create a checksum.

The problem is that some bytes get corrupted in bytes between the two streams.

Main code:

//..
// Initialization and creation of iv, aesKey
//..

in = new FileInputStream(fromPath);
out = new FileOutputStream(toPath);

//..
// Some code for generate the header and write it to out
//..
double totalBytes = new Long(archivo.length()).doubleValue();
int bloquesHilo = new Double(Math.ceil(totalBytes/(AESCrypt.NUM_THREADS*AESCrypt.BLOCK_SIZE))).intValue();
int offset_write = new Long((out.getChannel()).position()).intValue();

for (int i = 0; i < AESCrypt.NUM_THREADS; i++)
{
    int offset = bloquesHilo*AESCrypt.BLOCK_SIZE*i;
    HiloCrypt hilo = new HiloCrypt(fromPath, toPath, ivSpec, aesKey, offset, offsetInicio, bloquesHilo, this);
    hilo.start();
}

      

Thread code (HiloCrypt class): Public class HiloCrypt extends Thread {

    private RandomAccessFile in;
    private RandomAccessFile out;

    private Cipher cipher;
    private Mac hmac;
    private IvParameterSpec ivSpec2;
    private SecretKeySpec aesKey2;

    private Integer num_blocks;
    private Integer offset_read;
    private Integer offset_write;

    private AESCrypt parent;

    public HiloCrypt(String input, String output, IvParameterSpec ivSpec, SecretKeySpec aesKey, Integer offset_thread, Integer offset_write, Integer blocks, AESCrypt parent2) 
    {
        try
        {
                        // If i don't use RandomAccessFile there is a problem copying data
            this.in = new RandomAccessFile(input, "r");
            this.out = new RandomAccessFile(output, "rw");

            int total_offset_write = offset_write + offset_thread;

                        // Adjust the offset for reading and writing 
            this.out.seek(total_offset_write);
            this.in.seek(offset_thread);

            this.ivSpec2 = ivSpec;
            this.aesKey2 = aesKey;

            this.cipher = Cipher.getInstance(AESCrypt.CRYPT_TRANS);
            this.hmac = Mac.getInstance(AESCrypt.HMAC_ALG);

            this.num_blocks = blocks;
            this.offset_read = offset_thread;
            this.offset_write = total_offset_write;
            this.parent = parent2;

        } catch (Exception e)
        {
            System.err.println(e);
            return;
        }
    }


    public void run()
        {
        int len, last,block_counter,total = 0;
        byte[] text = new byte[AESCrypt.BLOCK_SIZE];

        try{
            // Start encryption objects
            this.cipher.init(Cipher.ENCRYPT_MODE, this.aesKey2, this.ivSpec2);
            this.hmac.init(new SecretKeySpec(this.aesKey2.getEncoded(), AESCrypt.HMAC_ALG));

            while ((len = this.in.read(text)) > 0 && block_counter < this.num_blocks) 
            {
                this.cipher.update(text, 0, AESCrypt.BLOCK_SIZE, text);
                this.hmac.update(text);

                // Write the block
                this.out.write(text);

                last = len;
                total+=len;

                block_counter++;
            }

            if (len < 0) // If it the last block, calculate the HMAC
            {
                last &= 0x0f;
                this.out.write(last);

                this.out.seek(this.offset_write-this.offset_read);

                while ((len = this.out.read(text)) > 0) 
                {
                    this.hmac.update(text);
                }

                // write last block of HMAC
                text=this.hmac.doFinal();
                this.out.write(text);
            }

                        // Close streams
            this.in.close();
            this.out.close();

                        // Code to notify the end of the thread
        }
        catch(Exception e)
        {
            System.err.println("Hola!");
            System.err.println(e);
        }
    }
}

      

With this code, if I only execute 1 stream, encryption / decryption is fine, but with 2+ streams, there is a problem with bytes in the zone between thread tasks, the data there gets corrupted and the checksum also fails.

I am trying to do this with threads because it is approaching 2x faster than a single thread, I think it must be due to processing, not file access.

As non-essential data, it compresses 250MB of data in 43 seconds on MB Air. Good time?

+3


source to share


3 answers


AESCrypt is not thread safe . You cannot use multiple threads with it.



Generally speaking, encryption code is rarely thread safe, as it requires complex mathematics to create secure output. AES itself is relatively fast, if you need faster speeds, consider vertical scaling or hardware accelerators as a first step. Later, you can add more servers to encrypt different files at the same time (scaling out).

+5


source


It doesn't make sense to use more than 1 thread for HMAC because 1) it has to be computed sequentially and 2) access to I / OR / W is much slower than actual computation of HMAC

For AES, it may be a good idea to use multiple streams when using CNT mode or other chaining modes that do not require knowledge of previous blocks of data.



how to move a question to crypto-stackexchange?

+1


source


Basically you want a multithreading operation that is inherently sequential .

Stream cipher

cannot be parallel because each block depends on the completion of the previous block. This way you can encrypt multiple files in parallel independently of each other with little performance gain, especially if the files are in memory rather than on disk, but you cannot encrypt a single file using multiple cores.

As I see you are using the method update

. I am not an expert in Java cryptography, but even the method name tells me that the encryption algorithm contains state : "multithreading" and "state" are not friends, you need to deal with the state of the flow control.

The race condition explains why the blocks are damaged.

0


source







All Articles