2

We are having an android app which a decrypting and encrypting large (up to 100MB) files over HTTP-Streams.

Therefore, we are using CipherInputStreams and CipherOutputStreams which works fine for AES/CBC/PKCS7Padding. We recently switched to AES/GCM/NoPadding. Now the encryption and decryption is inacceptable slow for files over roughly 50MB.

Debugging into the android source code, reveals the issues: https://android.googlesource.com/platform/libcore/+/master/ojluni/src/main/java/javax/crypto/CipherInputStream.java#112

This method has byte buffer "oBuffer" which is reallocated and increased by 512bits until it can hold the whole message (see line: https://android.googlesource.com/platform/libcore/+/master/ojluni/src/main/java/javax/crypto/CipherInputStream.java#121)

I am aware of the note over this method which stated that in AEAD ciphers the whole message has to be buffered. This is one issue, because we cannot hold the whole message into a memory buffer. Another issue is that the oBuffer is constantly reallocated.

Is there any solution for using GCM with a streaming API?

Timo
  • 357
  • 1
  • 3
  • 7
  • 1
    Divide the file into parts. The problem is due to the authentication tag calculation. – kelalaka Jan 29 '19 at 10:08
  • thx. but if a split the file into parts, the parts can be altered, e.g. a part can be doubled or left of, or the order of the parts can be altered. This would violate the goal of data integrity in my opinion. – Timo Jan 29 '19 at 12:27

1 Answers1

3

Splitting the file into the parts and chaining is a solution for you.

Assume that you divide the file into n parts. Encrypt each of them with AES-GCM with the following additions. Prefix each part before encryption as follows;

tag_0 = ''
for i from 1 to n
    ciphertextBlock_i, tag_i = AES-GCM( i:n || tag_i-1 || plaintextBlock_i)
  • prefix each part with the part number as i:n
  • prefix each part except the first one with the authentication tag of the previous part.

With these, you have now a chain that can be controlled after decryption. You can detect, additions, deletions. The order is under your control, you can send even without the order. However, you need to check the prefix.

You can also

  • add the part size, and
  • add the time of encryption, too if you fear from the replay attack.
kelalaka
  • 5,064
  • 5
  • 27
  • 44
  • Thank you again :) Nevertheless, I am little bit disappointed by this solution. Honestly I think, re-implementing such kind of blockchain is something the Crypto-API should do. The benefit of GCM in my case is questionable if the application developer has to implement integrity checks by themself. However, your approach seems very reasonable to me and I think we would stick to it. Thank you. – Timo Jan 29 '19 at 19:45
  • 1
    Unfortunately, some libraries implemented in this way, for the tag calculation they execute a second pass over data though it can be done in parallel. – kelalaka Jan 29 '19 at 19:49
  • 1
    Also, have a look at the Maarten's comments [here](https://stackoverflow.com/questions/54056151/difference-between-the-methods-update-and-dofinal-in-cipher/54056229#54056229) – kelalaka Jan 29 '19 at 19:53
  • 1
    Has this construct been studied? Can the block number and previous tag be provided to the AEAD primitive as additional authenticated data rather than as plaintext? – President James K. Polk Jan 29 '19 at 19:56
  • 1
    @JamesKPolk I've no information about this. I've created a solution for the question. I'll search about it. – kelalaka Jan 29 '19 at 20:00
  • 1
    @JamesKPolk I've talked with Maarten, in short, it was the designers decision so that people not plug and play with the CBC mode to ignore the tag calculation. Normally. then can code it to support parallel calculation in streaming mode. – kelalaka Jan 31 '19 at 14:24
  • @JamesKPolk here it is. – kelalaka Aug 14 '19 at 18:40