# HG changeset patch # User jjiang # Date 1531191597 -28800 # Node ID c18ca8590dfa2489fe3b2edeff6289123c421bd3 # Parent 1835f9fca15741fb0bc958500ce3fb053fa8928d 8203007: Address missing block coverage for ChaCha20 and Poly1305 algorithms Summary: Add unit tests for ChaCha20Cipher, ChaCha20Poly1305Parameters and Poly1305 Reviewed-by: xuelei, jnimeh diff -r 1835f9fca157 -r c18ca8590dfa test/jdk/com/sun/crypto/provider/Cipher/ChaCha20/unittest/ChaCha20CipherUnitTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/com/sun/crypto/provider/Cipher/ChaCha20/unittest/ChaCha20CipherUnitTest.java Tue Jul 10 10:59:57 2018 +0800 @@ -0,0 +1,252 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8153029 + * @library /test/lib + * @run main ChaCha20CipherUnitTest + * @summary Unit test for com.sun.crypto.provider.ChaCha20Cipher. + */ + +import java.nio.ByteBuffer; +import java.security.AlgorithmParameters; +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Arrays; + +import javax.crypto.Cipher; +import javax.crypto.spec.ChaCha20ParameterSpec; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import jdk.test.lib.Utils; + +public class ChaCha20CipherUnitTest { + + private static final byte[] NONCE + = Utils.toByteArray("012345670123456701234567"); + private static final SecretKeySpec KEY = new SecretKeySpec( + Utils.toByteArray( + "0123456701234567012345670123456701234567012345670123456701234567"), + "ChaCha20"); + private static final ChaCha20ParameterSpec CHACHA20_PARAM_SPEC + = new ChaCha20ParameterSpec(NONCE, 0); + private static final IvParameterSpec IV_PARAM_SPEC + = new IvParameterSpec(NONCE); + + public static void main(String[] args) throws Exception { + testTransformations(); + testInit(); + testAEAD(); + testGetBlockSize(); + } + + private static void testTransformations() throws Exception { + System.out.println("== transformations =="); + + checkTransformation("ChaCha20", true); + checkTransformation("ChaCha20/None/NoPadding", true); + checkTransformation("ChaCha20-Poly1305", true); + checkTransformation("ChaCha20-Poly1305/None/NoPadding", true); + + checkTransformation("ChaCha20/ECB/NoPadding", false); + checkTransformation("ChaCha20/None/PKCS5Padding", false); + checkTransformation("ChaCha20-Poly1305/ECB/NoPadding", false); + checkTransformation("ChaCha20-Poly1305/None/PKCS5Padding", false); + } + + private static void checkTransformation(String transformation, + boolean expected) throws Exception { + try { + Cipher.getInstance(transformation); + if (!expected) { + throw new RuntimeException( + "Unexpected transformation: " + transformation); + } else { + System.out.println("Expected transformation: " + transformation); + } + } catch (NoSuchAlgorithmException e) { + if (!expected) { + System.out.println("Unexpected transformation: " + transformation); + } else { + throw new RuntimeException("Unexpected fail: " + transformation, e); + } + } + } + + private static void testInit() throws Exception { + testInitOnCrypt(Cipher.ENCRYPT_MODE); + testInitOnCrypt(Cipher.DECRYPT_MODE); + testInitOnWrap(Cipher.WRAP_MODE); + testInitOnWrap(Cipher.UNWRAP_MODE); + } + + private static void testInitOnCrypt(int opMode) throws Exception { + System.out.println("== init (" + getOpModeName(opMode) + ") =="); + + Cipher.getInstance("ChaCha20").init(opMode, KEY, CHACHA20_PARAM_SPEC); + Cipher.getInstance("ChaCha20").init(opMode, KEY, + CHACHA20_PARAM_SPEC, new SecureRandom()); + + try { + Cipher.getInstance("ChaCha20").init(opMode, KEY, IV_PARAM_SPEC); + throw new RuntimeException("ChaCha20ParameterSpec is needed"); + } catch (InvalidAlgorithmParameterException e) { + System.out.println("Expected " + e); + } + + Cipher.getInstance("ChaCha20-Poly1305").init(opMode, KEY, + IV_PARAM_SPEC); + Cipher.getInstance("ChaCha20-Poly1305").init(opMode, KEY, + IV_PARAM_SPEC, new SecureRandom()); + + try { + Cipher.getInstance("ChaCha20-Poly1305").init(opMode, KEY, + CHACHA20_PARAM_SPEC); + throw new RuntimeException("IvParameterSpec is needed"); + } catch (InvalidAlgorithmParameterException e) { + System.out.println("Expected " + e); + } + + AlgorithmParameters algorithmParameters = + AlgorithmParameters.getInstance("ChaCha20-Poly1305"); + algorithmParameters.init( + new byte[] { 4, 12, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8 }); + try { + Cipher.getInstance("ChaCha20").init(opMode, KEY, + algorithmParameters, new SecureRandom()); + throw new RuntimeException( + "ChaCha20 cipher doesn't accept AlgorithmParameters"); + } catch (InvalidAlgorithmParameterException e) { + System.out.println("Expected " + e); + } + Cipher.getInstance("ChaCha20-Poly1305").init(opMode, KEY, + algorithmParameters, new SecureRandom()); + } + + private static void testInitOnWrap(int opMode) throws Exception { + String opModeName = getOpModeName(opMode); + System.out.println("== init (" + opModeName + ") =="); + + Cipher chacha20Cipher = Cipher.getInstance("ChaCha20"); + try { + chacha20Cipher.init(opMode, KEY, new SecureRandom()); + throw new RuntimeException( + "Unexpected opration mode: " + opModeName); + } catch (Exception e) { + if (e instanceof UnsupportedOperationException) { + System.out.println("Expected " + e); + } else { + throw new RuntimeException("Unexpected exception: " + e); + } + } + } + + private static void testAEAD() throws Exception { + byte[] expectedPlainttext = Utils.toByteArray("01234567"); + byte[] ciphertext = testUpdateAAD(Cipher.ENCRYPT_MODE, expectedPlainttext); + byte[] plaintext = testUpdateAAD(Cipher.DECRYPT_MODE, ciphertext); + if (!Arrays.equals(plaintext, expectedPlainttext)) { + System.out.println("ciphertext: " + Arrays.toString(ciphertext)); + System.out.println("plaintext: " + Arrays.toString(plaintext)); + throw new RuntimeException("AEAD failed"); + } + } + + private static byte[] testUpdateAAD(int opMode, byte[] input) + throws Exception { + String opModeName = getOpModeName(opMode); + System.out.println("== updateAAD (" + opModeName + ") =="); + + byte[] aad = Utils.toByteArray("0000"); + ByteBuffer aadBuf = ByteBuffer.wrap(aad); + + Cipher cipher = Cipher.getInstance("ChaCha20"); + cipher.init(opMode, KEY, CHACHA20_PARAM_SPEC); + try { + cipher.updateAAD(aadBuf); + throw new RuntimeException("ChaCha20 cipher cannot apply AAD"); + } catch (IllegalStateException e) { + System.out.println("Expected " + e); + } + + Cipher aeadCipher = Cipher.getInstance("ChaCha20-Poly1305"); + try { + aeadCipher.updateAAD(aadBuf); + throw new RuntimeException( + "Cannot update AAD on uninitialized Cipher"); + } catch (IllegalStateException e) { + System.out.println("Expected " + e); + } + aeadCipher.init(opMode, KEY, IV_PARAM_SPEC); + aeadCipher.update(input); + try { + aeadCipher.updateAAD(aad); + throw new RuntimeException( + "Cannot update AAD after plaintext/cipertext update"); + } catch (IllegalStateException e) { + System.out.println("Expected " + e); + } + + aeadCipher = Cipher.getInstance("ChaCha20-Poly1305"); + aeadCipher.init(opMode, KEY, IV_PARAM_SPEC); + aeadCipher.updateAAD(aadBuf); + return aeadCipher.doFinal(input); + } + + private static void testGetBlockSize() throws Exception { + testGetBlockSize(Cipher.ENCRYPT_MODE); + testGetBlockSize(Cipher.DECRYPT_MODE); + } + + private static void testGetBlockSize(int opMode) throws Exception { + System.out.println("== getBlockSize (" + getOpModeName(opMode) + ") =="); + + Cipher cipher = Cipher.getInstance("ChaCha20"); + cipher.init(opMode, KEY, CHACHA20_PARAM_SPEC); + if (cipher.getBlockSize() != 0) { + throw new RuntimeException("Block size must be 0"); + } + } + + private static String getOpModeName(int opMode) { + switch (opMode) { + case Cipher.ENCRYPT_MODE: + return "ENCRYPT"; + + case Cipher.DECRYPT_MODE: + return "DECRYPT"; + + case Cipher.WRAP_MODE: + return "WRAP"; + + case Cipher.UNWRAP_MODE: + return "UNWRAP"; + + default: + return ""; + } + } +} diff -r 1835f9fca157 -r c18ca8590dfa test/jdk/com/sun/crypto/provider/Cipher/ChaCha20/unittest/ChaCha20Poly1305ParametersUnitTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/com/sun/crypto/provider/Cipher/ChaCha20/unittest/ChaCha20Poly1305ParametersUnitTest.java Tue Jul 10 10:59:57 2018 +0800 @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8153029 + * @library /test/lib + * @run main ChaCha20Poly1305ParametersUnitTest + * @summary Unit test for com.sun.crypto.provider.ChaCha20Poly1305Parameters. + */ + +import java.io.IOException; +import java.security.AlgorithmParameters; +import java.security.spec.InvalidParameterSpecException; +import java.util.Arrays; + +import javax.crypto.spec.ChaCha20ParameterSpec; +import javax.crypto.spec.IvParameterSpec; + +public class ChaCha20Poly1305ParametersUnitTest { + + private static final String ALGORITHM = "ChaCha20-Poly1305"; + + private static final byte[] NONCE = { + 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8 }; + private static final byte[] PARAM = { + 4, 12, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8 }; + + private static final byte[] BAD_NONCE = { + 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + private static final byte[] BAD_PARAM = { + 4, 13, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + public static void main(String[] args) throws Exception { + testInit(); + testGetParameterSpec(); + testGetEncoded(); + } + + private static void testInit() throws Exception { + System.out.println("== init =="); + + AlgorithmParameters ap = AlgorithmParameters.getInstance(ALGORITHM); + ap.init(new IvParameterSpec(NONCE)); + System.out.println("AlgorithmParameters: " + ap); + + ap = AlgorithmParameters.getInstance(ALGORITHM); + ap.init(PARAM); + + ap = AlgorithmParameters.getInstance(ALGORITHM); + try { + ap.init(new ChaCha20ParameterSpec(NONCE, 0)); + throw new RuntimeException("IvParameterSpec is needed"); + } catch (InvalidParameterSpecException e) { + System.out.println("Expected " + e); + } + + ap = AlgorithmParameters.getInstance(ALGORITHM); + try { + ap.init(new IvParameterSpec(BAD_NONCE)); + throw new RuntimeException("Nonce must be 96 bits in length"); + } catch (InvalidParameterSpecException e) { + System.out.println("Expected " + e); + } + + ap = AlgorithmParameters.getInstance(ALGORITHM); + try { + ap.init(BAD_PARAM); + throw new RuntimeException("Nonce must be 96 bits in length"); + } catch (IOException e) { + System.out.println("Expected " + e); + } + } + + private static void testGetParameterSpec() throws Exception { + System.out.println("== getParameterSpec =="); + + AlgorithmParameters ap = AlgorithmParameters.getInstance(ALGORITHM); + ap.init(PARAM); + + IvParameterSpec paramSpec = ap.getParameterSpec(IvParameterSpec.class); + byte[] nonce = paramSpec.getIV(); + System.out.println("Nonce: " + Arrays.toString(nonce)); + Arrays.equals(nonce, NONCE); + + try { + ap.getParameterSpec(ChaCha20ParameterSpec.class); + throw new RuntimeException("IvParameterSpec is needed"); + } catch (InvalidParameterSpecException e) { + System.out.println("Expected " + e); + } + } + + private static void testGetEncoded() throws Exception { + System.out.println("== getEncoded =="); + + AlgorithmParameters ap = AlgorithmParameters.getInstance(ALGORITHM); + ap.init(PARAM); + + byte[] defaultFormatEncoded = ap.getEncoded(); + System.out.println("Default format encoding: " + + Arrays.toString(defaultFormatEncoded)); + if (!Arrays.equals(defaultFormatEncoded, PARAM)) { + throw new RuntimeException("Default format encoding failed"); + } + + byte[] asn1FormatEncoded = ap.getEncoded("ASN.1"); + System.out.println("ASN.1 format encoding: " + + Arrays.toString(asn1FormatEncoded)); + if (!Arrays.equals(asn1FormatEncoded, PARAM)) { + throw new RuntimeException("ASN.1 format encoding failed"); + } + + byte[] nullFormatEncoded = ap.getEncoded(null); + System.out.println("Null format encoding: " + + Arrays.toString(nullFormatEncoded)); + if (!Arrays.equals(nullFormatEncoded, PARAM)) { + throw new RuntimeException("Null format encoding failed"); + } + + try { + ap.getEncoded("BAD"); + throw new RuntimeException("Format must be ASN.1"); + } catch (IOException e) { + System.out.println("Expected " + e); + } + } +} diff -r 1835f9fca157 -r c18ca8590dfa test/jdk/com/sun/crypto/provider/Cipher/ChaCha20/unittest/Poly1305UnitTestDriver.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/com/sun/crypto/provider/Cipher/ChaCha20/unittest/Poly1305UnitTestDriver.java Tue Jul 10 10:59:57 2018 +0800 @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8153029 + * @modules java.base/com.sun.crypto.provider + * @run main java.base/com.sun.crypto.provider.Poly1305UnitTest + * @summary Unit test for com.sun.crypto.provider.Poly1305. + */ diff -r 1835f9fca157 -r c18ca8590dfa test/jdk/com/sun/crypto/provider/Cipher/ChaCha20/unittest/java.base/com/sun/crypto/provider/Poly1305UnitTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/com/sun/crypto/provider/Cipher/ChaCha20/unittest/java.base/com/sun/crypto/provider/Poly1305UnitTest.java Tue Jul 10 10:59:57 2018 +0800 @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.crypto.provider; + +import java.nio.ByteBuffer; +import java.util.Arrays; + +import javax.crypto.spec.SecretKeySpec; + +public class Poly1305UnitTest { + + public static void main(String[] args) throws Exception { + byte[] key = new byte[] { + 28, -110, 64, -91, -21, 85, -45, -118, + -13, 51, -120, -122, 4, -10, -75, -16, + 71, 57, 23, -63, 64, 43, -128, 9, + -99, -54, 92, -68, 32, 112, 117, -64 + }; + + Poly1305 authenticator = new Poly1305(); + if (authenticator.engineGetMacLength() != 16) { + throw new RuntimeException( + "The length of Poly1305 MAC must be 16-bytes."); + } + + authenticator.engineInit(new SecretKeySpec(key, 0, 32, + "Poly1305"), null); + + byte[] message = new byte[] { + 39, 84, 119, 97, 115, 32, 98, 114, + 105, 108, 108, 105, 103, 44, 32, 97, + 110, 100, 32, 116, 104, 101, 32, 115, + 108, 105, 116, 104, 121, 32, 116, 111, + 118, 101, 115, 10, 68, 105, 100, 32, + 103, 121, 114, 101, 32, 97, 110, 100, + 32, 103, 105, 109, 98, 108, 101, 32, + 105, 110, 32, 116, 104, 101, 32, 119, + 97, 98, 101, 58, 10, 65, 108, 108, + 32, 109, 105, 109, 115, 121, 32, 119, + 101, 114, 101, 32, 116, 104, 101, 32, + 98, 111, 114, 111, 103, 111, 118, 101, + 115, 44, 10, 65, 110, 100, 32, 116, + 104, 101, 32, 109, 111, 109, 101, 32, + 114, 97, 116, 104, 115, 32, 111, 117, + 116, 103, 114, 97, 98, 101, 46 + }; + + authenticator.engineUpdate( + ByteBuffer.wrap(Arrays.copyOfRange(message, 0, 8))); + authenticator.engineUpdate(message, 8, 104); + authenticator.engineUpdate(message, 112, 7); + for (int i = 119; i < message.length; i++) { + authenticator.engineUpdate(message[i]); + } + + byte[] tag = authenticator.engineDoFinal(); + byte[] expectedTag = new byte[] { + 69, 65, 102, -102, 126, -86, -18, 97, + -25, 8, -36, 124, -68, -59, -21, 98 + }; + if (!Arrays.equals(tag, expectedTag)) { + throw new RuntimeException( + "Unexpected tag: " + Arrays.toString(tag)); + } + } +}