1 /* 2 * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /** 25 * @test 26 * @bug 8224997 27 * @summary ChaCha20-Poly1305 TLS cipher suite decryption throws ShortBufferException 28 * @library /test/lib 29 * @build jdk.test.lib.Convert 30 * @run main OutputSizeTest 31 */ 32 33 import java.nio.ByteBuffer; 34 import java.security.GeneralSecurityException; 35 import java.security.Key; 36 import java.security.SecureRandom; 37 import javax.crypto.Cipher; 38 import javax.crypto.KeyGenerator; 39 import javax.crypto.spec.ChaCha20ParameterSpec; 40 import javax.crypto.spec.IvParameterSpec; 41 42 public class OutputSizeTest { 43 44 private static final SecureRandom SR = new SecureRandom(); 45 main(String args[])46 public static void main(String args[]) throws Exception { 47 testCC20GetOutSize(); 48 testCC20P1305GetOutSize(); 49 testMultiPartAEADDec(); 50 } 51 testCC20GetOutSize()52 private static void testCC20GetOutSize() 53 throws GeneralSecurityException { 54 boolean result = true; 55 KeyGenerator kg = KeyGenerator.getInstance("ChaCha20", "SunJCE"); 56 kg.init(256); 57 58 // ChaCha20 encrypt 59 Cipher cc20 = Cipher.getInstance("ChaCha20", "SunJCE"); 60 cc20.init(Cipher.ENCRYPT_MODE, kg.generateKey(), 61 new ChaCha20ParameterSpec(getRandBuf(12), 10)); 62 63 testOutLen(cc20, 0, 0); 64 testOutLen(cc20, 5, 5); 65 testOutLen(cc20, 5120, 5120); 66 // perform an update, then test with a final block 67 byte[] input = new byte[5120]; 68 SR.nextBytes(input); 69 cc20.update(input); 70 testOutLen(cc20, 1024, 1024); 71 72 // Decryption lengths should be calculated the same way as encryption 73 cc20.init(Cipher.DECRYPT_MODE, kg.generateKey(), 74 new ChaCha20ParameterSpec(getRandBuf(12), 10)); 75 testOutLen(cc20, 0, 0); 76 testOutLen(cc20, 5, 5); 77 testOutLen(cc20, 5120, 5120); 78 // perform an update, then test with a final block 79 cc20.update(input); 80 testOutLen(cc20, 1024, 1024); 81 } 82 testCC20P1305GetOutSize()83 private static void testCC20P1305GetOutSize() 84 throws GeneralSecurityException { 85 KeyGenerator kg = KeyGenerator.getInstance("ChaCha20", "SunJCE"); 86 kg.init(256); 87 88 // ChaCha20 encrypt 89 Cipher cc20 = Cipher.getInstance("ChaCha20-Poly1305", "SunJCE"); 90 cc20.init(Cipher.ENCRYPT_MODE, kg.generateKey(), 91 new IvParameterSpec(getRandBuf(12))); 92 93 // Encryption lengths are calculated as the input length plus the tag 94 // length (16). 95 testOutLen(cc20, 0, 16); 96 testOutLen(cc20, 5, 21); 97 testOutLen(cc20, 5120, 5136); 98 // perform an update, then test with a final block 99 byte[] input = new byte[5120]; 100 SR.nextBytes(input); 101 cc20.update(input); 102 testOutLen(cc20, 1024, 1040); 103 104 // Decryption lengths are handled differently for AEAD mode. The length 105 // should be zero for anything up to and including the first 16 bytes 106 // (since that's the tag). Anything above that should be the input 107 // length plus any unprocessed input (via update calls), minus the 108 // 16 byte tag. 109 cc20.init(Cipher.DECRYPT_MODE, kg.generateKey(), 110 new IvParameterSpec(getRandBuf(12))); 111 testOutLen(cc20, 0, 0); 112 testOutLen(cc20, 5, 0); 113 testOutLen(cc20, 16, 0); 114 testOutLen(cc20, 5120, 5104); 115 // Perform an update, then test with a the length of a final chunk 116 // of data. 117 cc20.update(input); 118 testOutLen(cc20, 1024, 6128); 119 } 120 testMultiPartAEADDec()121 private static void testMultiPartAEADDec() throws GeneralSecurityException { 122 KeyGenerator kg = KeyGenerator.getInstance("ChaCha20", "SunJCE"); 123 kg.init(256); 124 Key key = kg.generateKey(); 125 IvParameterSpec ivps = new IvParameterSpec(getRandBuf(12)); 126 127 // Encrypt some data so we can test decryption. 128 byte[] pText = getRandBuf(2048); 129 ByteBuffer pTextBase = ByteBuffer.wrap(pText); 130 131 Cipher enc = Cipher.getInstance("ChaCha20-Poly1305", "SunJCE"); 132 enc.init(Cipher.ENCRYPT_MODE, key, ivps); 133 ByteBuffer ctBuf = ByteBuffer.allocateDirect( 134 enc.getOutputSize(pText.length)); 135 enc.doFinal(pTextBase, ctBuf); 136 137 // Create a new direct plain text ByteBuffer which will catch the 138 // decrypted data. 139 ByteBuffer ptBuf = ByteBuffer.allocateDirect(pText.length); 140 141 // Set the cipher text buffer limit to roughly half the data so we can 142 // do an update/final sequence. 143 ctBuf.position(0).limit(1024); 144 145 Cipher dec = Cipher.getInstance("ChaCha20-Poly1305", "SunJCE"); 146 dec.init(Cipher.DECRYPT_MODE, key, ivps); 147 dec.update(ctBuf, ptBuf); 148 System.out.println("CTBuf: " + ctBuf); 149 System.out.println("PTBuf: " + ptBuf); 150 ctBuf.limit(ctBuf.capacity()); 151 dec.doFinal(ctBuf, ptBuf); 152 153 ptBuf.flip(); 154 pTextBase.flip(); 155 System.out.println("PT Base:" + pTextBase); 156 System.out.println("PT Actual:" + ptBuf); 157 158 if (pTextBase.compareTo(ptBuf) != 0) { 159 StringBuilder sb = new StringBuilder(); 160 sb.append("Plaintext mismatch: Original: "). 161 append(pTextBase.toString()).append("\nActual :"). 162 append(ptBuf); 163 throw new RuntimeException(sb.toString()); 164 } 165 } 166 testOutLen(Cipher c, int inLen, int expOut)167 private static void testOutLen(Cipher c, int inLen, int expOut) { 168 int actualOut = c.getOutputSize(inLen); 169 if (actualOut != expOut) { 170 throw new RuntimeException("Cipher " + c + ", in: " + inLen + 171 ", expOut: " + expOut + ", actual: " + actualOut); 172 } 173 } 174 getRandBuf(int len)175 private static byte[] getRandBuf(int len) { 176 byte[] buf = new byte[len]; 177 SR.nextBytes(buf); 178 return buf; 179 } 180 } 181 182