JDK-8228975 : Cannot use ChaCha20-Poly1305 in a CipherInputStream
  • Type: Bug
  • Component: security-libs
  • Sub-Component: javax.crypto
  • Affected Version: 11,13,14
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: generic
  • CPU: x86_64
  • Submitted: 2019-07-30
  • Updated: 2019-08-01
  • Resolved: 2019-08-01
Related Reports
Duplicate :  
Description
ADDITIONAL SYSTEM INFORMATION :
MacOS X 10.11, but also CentOS 7.
java version "11.0.1" 2018-10-16 LTS
Java(TM) SE Runtime Environment 18.9 (build 11.0.1+13-LTS)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.1+13-LTS, mixed mode)

Also fails on 11.0.4 and 12

A DESCRIPTION OF THE PROBLEM :
Trying to use ChaCha20-Poly1305 as cipher for a CipherInputStream always fails with a ShortBufferException.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
The attached source code tests the use of AES/GCM, ChaCha20 and ChaCha20-Poly1305 as parameters for a CipherInputStream. AES/GCM and ChaCha20 work correctly but not ChaCha20-Poly1305.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
ChaCha20-Poly1305 can decrypt correctly.
ACTUAL -
ChaCha20-Poly1305 Decryption fails with
Exception in thread "main" java.lang.RuntimeException: javax.crypto.ShortBufferException: Output buffer too small
	at java.base/com.sun.crypto.provider.ChaCha20Cipher.engineDoFinal(ChaCha20Cipher.java:703)
	at java.base/javax.crypto.Cipher.doFinal(Cipher.java:2085)
	at java.base/javax.crypto.CipherInputStream.getMoreData(CipherInputStream.java:125)
	at java.base/javax.crypto.CipherInputStream.read(CipherInputStream.java:242)
	at java.base/java.io.InputStream.readNBytes(InputStream.java:396)
	at java.base/java.io.InputStream.readAllBytes(InputStream.java:333)
	at TestChaCha20.testChaCha20Poly1305(TestChaCha20.java:88)
	at TestChaCha20.main(TestChaCha20.java:31)
Caused by: javax.crypto.ShortBufferException: Output buffer too small
	at java.base/com.sun.crypto.provider.ChaCha20Cipher$EngineAEADDec.doFinal(ChaCha20Cipher.java:1360)
	at java.base/com.sun.crypto.provider.ChaCha20Cipher.engineDoFinal(ChaCha20Cipher.java:701)
	... 7 more


Note that if instead of using readAllBytes(), read(data) is used, the exception is in close():

Exception in thread "main" java.lang.RuntimeException: javax.crypto.ShortBufferException: Output buffer too small
	at java.base/com.sun.crypto.provider.ChaCha20Cipher.engineDoFinal(ChaCha20Cipher.java:703)
	at java.base/javax.crypto.Cipher.doFinal(Cipher.java:2085)
	at java.base/javax.crypto.CipherInputStream.close(CipherInputStream.java:323)
	at TestChaCha20.testChaCha20Poly1305(TestChaCha20.java:91)
	at TestChaCha20.main(TestChaCha20.java:31)
Caused by: javax.crypto.ShortBufferException: Output buffer too small
	at java.base/com.sun.crypto.provider.ChaCha20Cipher$EngineAEADDec.doFinal(ChaCha20Cipher.java:1360)
	at java.base/com.sun.crypto.provider.ChaCha20Cipher.engineDoFinal(ChaCha20Cipher.java:701)
	... 4 more

In this case the returned byte array is all zeros, as if nothing was actually decrypted.

---------- BEGIN SOURCE ----------
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.ChaCha20ParameterSpec;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

class TestChaCha20 {
    public static void main(String[] args)
            throws NoSuchPaddingException, InvalidKeyException, NoSuchAlgorithmException, IOException,
                   BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException {
        testAESGCM();
        testChaCha20();
        testChaCha20Poly1305();
    }

    static void testAESGCM()
            throws InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException, BadPaddingException,
                   IllegalBlockSizeException, InvalidAlgorithmParameterException, IOException {
        byte[] key = new byte[16];
        byte[] iv = new byte[12];
        SecretKey secretKey = new SecretKeySpec(key, "AES");
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        GCMParameterSpec parameterSpec = new GCMParameterSpec(128, iv);
        cipher.init(Cipher.ENCRYPT_MODE, secretKey, parameterSpec);

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        final CipherOutputStream csout = new CipherOutputStream(out, cipher);
        byte[] clearText = "0123456789".getBytes();
        csout.write(clearText);
        csout.close();
        byte[] cipherText = out.toByteArray();
        System.out.println("AES/GCM Cipher[" + cipherText.length +  "]:"
                                   + (new BigInteger(1, cipherText)).toString(16));

        ByteArrayInputStream in = new ByteArrayInputStream(cipherText);
        Cipher icipher = Cipher.getInstance("AES/GCM/NoPadding");
        icipher.init(Cipher.DECRYPT_MODE, secretKey, parameterSpec);
        byte[] data;
        try (InputStream cin = new CipherInputStream(in, icipher)) {
            data = cin.readAllBytes();
            System.out.println("AES/GCM Clear[" + data.length +  "]:" + (new BigInteger(1, data)).toString(16));
        }
    }

    static void testChaCha20Poly1305()
            throws InvalidAlgorithmParameterException, InvalidKeyException, NoSuchPaddingException,
                   NoSuchAlgorithmException, IOException, BadPaddingException, IllegalBlockSizeException {
        byte[] key = new byte[32];
        byte[] nonceBytes = new byte[12];
        Cipher cipher = Cipher.getInstance("ChaCha20-Poly1305/None/NoPadding");
        AlgorithmParameterSpec ivParameterSpec = new IvParameterSpec(nonceBytes);
        SecretKeySpec keySpec = new SecretKeySpec(key, "ChaCha20-Poly1305");
        cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivParameterSpec);

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        final CipherOutputStream csout = new CipherOutputStream(out, cipher);
        byte[] clearText = "0123456789".getBytes();
        csout.write(clearText);
        csout.close();
        byte[] cipherText = out.toByteArray();
        System.out.println("ChaCha20-Poly1305 Cipher[" + cipherText.length +  "]:"
                                   + (new BigInteger(1, cipherText)).toString(16));

        ByteArrayInputStream in = new ByteArrayInputStream(cipherText);
        Cipher icipher = Cipher.getInstance("ChaCha20-Poly1305/None/NoPadding");
        icipher.init(Cipher.DECRYPT_MODE, keySpec, ivParameterSpec);

        byte[] data;
        try (InputStream csin = new CipherInputStream(in, icipher)) {
            data = csin.readAllBytes();
            System.out.println("ChaCha20-Poly1305 Clear[" + data.length +  "]:"
                                       + (new BigInteger(1, data)).toString(16));
        }
    }

    static void testChaCha20()
            throws InvalidAlgorithmParameterException, InvalidKeyException, NoSuchPaddingException,
                   NoSuchAlgorithmException, IOException, BadPaddingException, IllegalBlockSizeException {
        byte[] key = new byte[32];
        byte[] nonceBytes = new byte[12];
        Cipher cipher = Cipher.getInstance("ChaCha20/None/NoPadding");
        ChaCha20ParameterSpec ivParameterSpec = new ChaCha20ParameterSpec(nonceBytes, 1);
        SecretKeySpec keySpec = new SecretKeySpec(key, "ChaCha20");
        cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivParameterSpec);

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        final CipherOutputStream csout = new CipherOutputStream(out, cipher);
        byte[] clearText = "0123456789".getBytes();
        csout.write(clearText);
        csout.close();
        byte[] cipherText = out.toByteArray();
        System.out.println("ChaCha20 Cipher[" + cipherText.length +  "]:"
                                   + (new BigInteger(1, cipherText)).toString(16));

        ByteArrayInputStream in = new ByteArrayInputStream(cipherText);
        Cipher icipher = Cipher.getInstance("ChaCha20/None/NoPadding");
        icipher.init(Cipher.DECRYPT_MODE, keySpec, ivParameterSpec);

        byte[] data;
        try (InputStream csin = new CipherInputStream(in, icipher)) {
            data = csin.readAllBytes();
            System.out.println("ChaCha20 Clear[" + data.length +  "]:"
                                       + (new BigInteger(1, data)).toString(16));
        }
    }
}
---------- END SOURCE ----------

FREQUENCY : always



Comments
This is similar to JDK-8224997. Closing as duplicate.
01-08-2019