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