-1

As the title say i'm trying to encrypt a file using 2 thread and to decrypt using 1 thread. Since encryption isn't thread safe i use FileChannel to specify the position where to read in the second thread. I am using a 512 byte buffer so i divide the file size by 1024 thus obtaining the iteration to do in the while to reach the end of the first half of the file. The second thread start immediately after the end of the first one and iterate until the end of the file. The decryption function read only one file at time appending the second atthe end of the first.

I've tested with an image but unfortunately after the decryption only the first half of the image can be seen the rest is white. At the end of the decryption i get Error while decrypting: java.io.IOException: javax.crypto.BadPaddingException

EDIT1: I think i have made a step forward, now i am able to achieve multi thread encryption by reading the file in 2 different places but i'm still getting Error while decrypting: java.io.IOException: javax.crypto.BadPaddingException after decryption

    public void encrypt(File inputFile, File outputFile, String secret, int threadNum, long iteration) 
    {       
        try
        {
            setKey(secret);
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
            
            FileInputStream fis = new FileInputStream(inputFile);
            FileOutputStream fos = new FileOutputStream(outputFile);
            
            byte[] buffer = new byte[512];
            long val = inputFile.length()/100, numIteration = 0;
            int count;
            if(threadNum == 1)
            {
                while(true)
                {
                    if(numIteration == iteration)
                    {
                        fis.read(buffer);
                        fos.write(cipher.doFinal(buffer));
                        System.out.println("Dofinal 1°");
                        break;
                    }
                    else {
                        fis.read(buffer);
                        fos.write(cipher.update(buffer));
                    }
                    System.out.println("Num iteration: "+numIteration);
                    numIteration++;
                }
            }
            else if(threadNum == 2)
            {
                while(numIteration <= iteration)
                {
                    if(numIteration <= iteration/2)
                    {
                        fis.skip(512);
                    }
                    else if(numIteration >= iteration/2) {
                        fis.read(buffer);
                        fos.write(cipher.update(buffer));
                        System.out.println("Iteration"+numIteration);
                    }
                    else if(numIteration == iteration)
                    {
                        fis.read(buffer);
                        fos.write(cipher.doFinal(new byte[(int) inputFile.length()%512]));
                        break;
                    }
                    System.out.println("NumIteration: "+numIteration);
                    numIteration++;
                }
            }
        }
        catch (Exception e) 
        {
            System.out.println("Error while encrypting: " + e.toString());
        }
    }

    public void decrypt(File inputFile[], File outputFile, String secret) 
    {
        System.out.println("Decryption");
        try
        {
            setKey(secret);
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, secretKey);
            
            for(int i = 0; i < 2; i++)
            {
                CipherInputStream in = new CipherInputStream(new FileInputStream(inputFile[i]), cipher);
                CountingInputStream cis = new CountingInputStream(in);
                
                FileOutputStream out;
                
                if(i == 0)
                    out = new FileOutputStream(outputFile);
                else 
                    out = new FileOutputStream(outputFile, true);

                CountingOutputStream cos = new CountingOutputStream(out);

                int count;
                double val = (inputFile[i].length()/100);
                byte[] buffer = new byte[512];
                while((count = cis.read(buffer)) != -1)
                {
                    cos.write(buffer, 0, count);
                    cos.flush();
                }
                cis.close();
                cos.close();
            }
        } 
        catch (Exception e) 
        {
            System.out.println("Error while decrypting: " + e.toString());
        }
    }
Marcus34
  • 1
  • 2
  • `FileChannel` isn't thread-safe either, and as the disk itself isn't multi-threaded there is no gain to be expected by multi-threading the encryption. – user207421 Sep 14 '20 at 00:21
  • Why do you think that "Encryption" isn't Thread-safe? The Cipher-object is NOT Thread-safe so you create and use the Cipher-object in each thread. – Michael Fehr Sep 14 '20 at 10:00
  • @MarquisofLorne i'm using an nvme ssd as storage so should be possible achieve multithreading read, but then at this point i don't know what cause this problem during decryption, if filechannel isn't set up properly or something else – Marcus34 Sep 14 '20 at 13:33
  • I would question this way of working too, but let's pretend. This *may* work thanks to the use of ECB (no previous state is needded for the Cipher to process a block) and PKCS5 (same reason). But this won't work as long as 1) you have thread unsafe computation/publication/reset of the `key` and `secretKey` static variables 2) you have unordered writes (block N finishes before block N-1 ?), 3) you do not bound your reads to the block size, 4) nobody controls calls to `doFinal()` on the Cipher (this will be hard). Ultim: how safe will it be? – GPI Sep 14 '20 at 14:10

1 Answers1

0

I managed to achieve dual-thread encryption, boosting performance of about 28% on a 48,5 mb file (from 0,57 to 0,41 s) using an i5 10210u 4c 8t. Should be possible achieve higher performance by raising the number of thread according with the number of cores of the CPU

Here is the code:

    public void encrypt(File inputFile, File outputFile, String secret, int threadNum, long iteration) 
    {       
        try
        {
            setKey(secret);
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
            
            FileInputStream fis = new FileInputStream(inputFile);
            FileOutputStream fos = new FileOutputStream(outputFile);
            
            byte[] buffer = new byte[512];
            long numIteration = 1;
            
            if(threadNum == 1)
            {
                while(true)
                {
                    if(numIteration == iteration)
                    {
                        fis.read(buffer);
                        fos.write(cipher.doFinal(buffer));
                        break;
                    }
                    else 
                    {
                        fis.read(buffer);
                        fos.write(cipher.update(buffer));
                    }
                    numIteration++;
                }
            }
            else if(threadNum == 2)
            {
                while(numIteration <= iteration)
                {
                    if(numIteration == iteration)
                    {
                        byte[] f = new byte[(int) inputFile.length()%512];
                        fis.read(f);
                        fos.write(cipher.doFinal(f));
                        break;
                    }
                    else if(numIteration < iteration/2)
                    {
                        fis.skip(512);
                    }
                    else if(numIteration >= iteration/2 && numIteration < iteration) 
                    {
                        fis.read(buffer);
                        fos.write(cipher.update(buffer));
                    }
                    numIteration++;
                }
            }
            fis.close();
            fos.close();
        }
        catch (Exception e) 
        {
            System.out.println("Error while encrypting: " + e.toString());
        }
    }

    public void decrypt(File inputFile[], File outputFile, String secret) 
    {
        try
        {
            setKey(secret);
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, secretKey);
            
            for(int i = 0; i < 2; i++)
            {               
                CipherInputStream in = new CipherInputStream(new FileInputStream(inputFile[i]), cipher);
                CountingInputStream cis = new CountingInputStream(in);
                
                FileOutputStream out;
                
                if(i == 0)
                    out = new FileOutputStream(outputFile);
                else 
                    out = new FileOutputStream(outputFile, true);

                CountingOutputStream cos = new CountingOutputStream(out);

                int count;
                double val = (inputFile[i].length()/100);
                byte[] buffer = new byte[512];
                
                while((count = cis.read(buffer)) != -1)
                {
                    cos.write(buffer, 0, count);
                    cos.flush();
                    System.out.println("Percentuale: "+cos.getCount()/val+"%");
                }
                cis.close();
                cos.close();
            }
        } 
        catch (Exception e) 
        {
            System.out.println("Error while decrypting: " + e.toString());
        }
    }
Marcus34
  • 1
  • 2
  • I'd rather say "most of the time the code does not fail". There still is a race condition in the setting of the key. There are no safeguards that reads reach their desired length (512 bytes), thus that block size integrity is maintained. The output is two separate files where there was one input, which will cost time down the road. This does not scale dynamically to the CPU power available (which would create even more files to merge), nor does it scale to other key sizes, block modes or padding modes. This is fine as an exercise but i would not trust it. – GPI Sep 16 '20 at 16:45
  • @GPI I plan obviously to use a multi thread encryption to process large file, the example was with one of 43 mb but using bigger ones the time improvement goes near 33%(taking into account only encryption obviously) so theoretically the block size problem doesn't really affect me. During decryption the files are appended (not decrypted and then merged) so there isn't a time difference (in decryption). The program works in CBC mode too adding the `IvParameterSpec`. Regarding the race condition of the key can you elaborate more? – Marcus34 Sep 17 '20 at 00:12