0

I'm trying to write some bytes to a Gzip-compressed and ChaCha20-encrypted output stream to recreate an HmacBlock of a KDBX4 file. Below is how I declare and initialize the streams:

//needed for determining # of bytes written and resetting to 0 bytes when a certain amount are written
private ByteArrayOutputStream blockCipherTextOutputStream = new ByteArrayOutputStream();

//wraps blockCipherTextOutputStream
//bytes written here are ChaCha20 encrypted
private OutputStream encryptedOutputStream;

//wraps encryptedOutputStream
//bytes written here are in little-endian order
private LittleEndianDataOutputStream ledos; 

//creating a CipherOutputStream initialized with ChaCha20 encryption
//sets blockCipherTextOutputStream as the underlying OutputStream to be written to
encryptedOutputStream = kdbxHeader.createEncryptedStream(credentials.getKey(), blockCipherTextOutputStream);

//the OutputStream that writes directly to the file
//bytes eventually get written here in other methods
private OutputStream outputStream;
        
if(kdbxHeader.getCompressionFlags().equals(KdbxHeader.CompressionFlags.GZIP)) {
    /*
     * this seems to be adding 10 bytes to the stream to start
     * writing to the stream seemingly does nothing afterwards
     */
     encryptedOutputStream = new GZIPOutputStream(encryptedOutputStream);            
}
                
ledos = new LittleEndianDataOutputStream(encryptedOutputStream);

When creating the encrypted output stream, a method is called in the KdbxHeader class which in turn calls to a method in another class called ChaCha. Below is the method in ChaCha which actually creates (and returns) an encrypted output stream:

@Override
    public OutputStream getEncryptedOutputStream(OutputStream decryptedOutputStream, byte[] key, byte[] iv) {
        final ParametersWithIV keyAndIV = new ParametersWithIV(new KeyParameter(key), iv);
        StreamCipher cipher = new ChaCha7539Engine();
        cipher.init(true, keyAndIV);
        return new CipherOutputStream(decryptedOutputStream, cipher);
    }

Hopefully that's enough code to go on, I can add more if necessary but this is part of a larger API so there is a lot going on. encryptedOutputStream only starts (and doesn't expand from) 10 bytes when I try to compress it; otherwise, it starts empty (as it should) and I can write to it.

Even after initializing blockCipherTextOutputStream with a buffer size of 80000, the write attempts still don't seem to do anything as only the 10 bytes in encryptedOutputStream to start are visible in a hex editor. Why is this happening and what am I doing wrong here?

Siguza
  • 21,155
  • 6
  • 52
  • 89
noG23
  • 93
  • 2
  • 7
  • 1
    Are you ever closing the stream? – user207421 Aug 02 '22 at 23:55
  • 1
    Yup. Flushing is not sufficient. The final block will only be written when the cipher stream is closed. – Stephen C Aug 03 '22 at 00:00
  • @user207421 Not encryptedOutputStream, no. With that stream, all I do is convert it to a byte array, write the byte array to outputStream, and then reset blockCipherTextOutputStream. When should I be closing encryptedOutputStream? – noG23 Aug 03 '22 at 00:22
  • @StephenC Should I close encryptedOutputStream before converting it to a byte array then? Sorry, I'm still not sure I understand. – noG23 Aug 03 '22 at 00:24
  • 1
    Yes. You should. Otherwise the byte array won't contain the complete content. The close of the cipher streams causes the final block to padded so that it can be encrypted and written. The [javadoc](https://docs.oracle.com/javase/7/docs/api/javax/crypto/CipherOutputStream.html#close()) says what happens. (The `doFinal` call ...) – Stephen C Aug 03 '22 at 00:32
  • @StephenC Closing encryptedOutputStream before converting blockCipherTextOutputStream seems to have worked, as there are more than 10 bytes visible in the hex editor now. Thanks for the help, would you mind making an answer? – noG23 Aug 03 '22 at 01:03
  • I did better. Closed it as a dup of an earlier question. – Stephen C Aug 03 '22 at 01:11

0 Answers0