1 /* 2 * Copyright (c) 2013, 2016, 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 package org.graalvm.compiler.hotspot.test; 24 25 import java.io.ByteArrayOutputStream; 26 import java.io.DataInputStream; 27 import java.io.IOException; 28 import java.io.InputStream; 29 import java.security.AlgorithmParameters; 30 import java.security.SecureRandom; 31 32 import javax.crypto.Cipher; 33 import javax.crypto.KeyGenerator; 34 import javax.crypto.SecretKey; 35 36 import org.junit.Assert; 37 import org.junit.Test; 38 39 import org.graalvm.compiler.code.CompilationResult; 40 import org.graalvm.compiler.hotspot.meta.HotSpotGraphBuilderPlugins; 41 42 import jdk.vm.ci.code.InstalledCode; 43 import jdk.vm.ci.meta.ResolvedJavaMethod; 44 45 /** 46 * Tests the intrinsification of certain crypto methods. 47 */ 48 public class HotSpotCryptoSubstitutionTest extends HotSpotGraalCompilerTest { 49 50 @Override 51 protected InstalledCode addMethod(ResolvedJavaMethod method, CompilationResult compResult) { 52 return getBackend().createDefaultInstalledCode(method, compResult); 53 } 54 55 SecretKey aesKey; 56 SecretKey desKey; 57 byte[] input; 58 ByteArrayOutputStream aesExpected = new ByteArrayOutputStream(); 59 ByteArrayOutputStream desExpected = new ByteArrayOutputStream(); 60 61 public HotSpotCryptoSubstitutionTest() throws Exception { 62 byte[] seed = {0x4, 0x7, 0x1, 0x1}; 63 SecureRandom random = new SecureRandom(seed); 64 KeyGenerator aesKeyGen = KeyGenerator.getInstance("AES"); 65 KeyGenerator desKeyGen = KeyGenerator.getInstance("DESede"); 66 aesKeyGen.init(128, random); 67 desKeyGen.init(168, random); 68 aesKey = aesKeyGen.generateKey(); 69 desKey = desKeyGen.generateKey(); 70 input = readClassfile16(getClass()); 71 72 aesExpected.write(runEncryptDecrypt(aesKey, "AES/CBC/NoPadding")); 73 aesExpected.write(runEncryptDecrypt(aesKey, "AES/CBC/PKCS5Padding")); 74 75 desExpected.write(runEncryptDecrypt(desKey, "DESede/CBC/NoPadding")); 76 desExpected.write(runEncryptDecrypt(desKey, "DESede/CBC/PKCS5Padding")); 77 } 78 79 @Test 80 public void testAESCryptIntrinsics() throws Exception { 81 if (compileAndInstall("com.sun.crypto.provider.AESCrypt", HotSpotGraphBuilderPlugins.aesEncryptName, HotSpotGraphBuilderPlugins.aesDecryptName)) { 82 ByteArrayOutputStream actual = new ByteArrayOutputStream(); 83 actual.write(runEncryptDecrypt(aesKey, "AES/CBC/NoPadding")); 84 actual.write(runEncryptDecrypt(aesKey, "AES/CBC/PKCS5Padding")); 85 Assert.assertArrayEquals(aesExpected.toByteArray(), actual.toByteArray()); 86 } 87 } 88 89 @Test 90 public void testCipherBlockChainingIntrinsics() throws Exception { 91 if (compileAndInstall("com.sun.crypto.provider.CipherBlockChaining", HotSpotGraphBuilderPlugins.cbcEncryptName, HotSpotGraphBuilderPlugins.cbcDecryptName)) { 92 ByteArrayOutputStream actual = new ByteArrayOutputStream(); 93 actual.write(runEncryptDecrypt(aesKey, "AES/CBC/NoPadding")); 94 actual.write(runEncryptDecrypt(aesKey, "AES/CBC/PKCS5Padding")); 95 Assert.assertArrayEquals(aesExpected.toByteArray(), actual.toByteArray()); 96 97 actual.reset(); 98 actual.write(runEncryptDecrypt(desKey, "DESede/CBC/NoPadding")); 99 actual.write(runEncryptDecrypt(desKey, "DESede/CBC/PKCS5Padding")); 100 Assert.assertArrayEquals(desExpected.toByteArray(), actual.toByteArray()); 101 } 102 } 103 104 /** 105 * Compiles and installs the substitution for some specified methods. Once installed, the next 106 * execution of the methods will use the newly installed code. 107 * 108 * @param className the name of the class for which substitutions are available 109 * @param methodNames the names of the substituted methods 110 * @return true if at least one substitution was compiled and installed 111 */ 112 private boolean compileAndInstall(String className, String... methodNames) { 113 if (!runtime().getVMConfig().useAESIntrinsics) { 114 return false; 115 } 116 Class<?> c; 117 try { 118 c = Class.forName(className); 119 } catch (ClassNotFoundException e) { 120 // It's ok to not find the class - a different security provider 121 // may have been installed 122 return false; 123 } 124 boolean atLeastOneCompiled = false; 125 for (String methodName : methodNames) { 126 if (compileAndInstallSubstitution(c, methodName) != null) { 127 atLeastOneCompiled = true; 128 } 129 } 130 return atLeastOneCompiled; 131 } 132 133 AlgorithmParameters algorithmParameters; 134 135 private byte[] encrypt(byte[] indata, SecretKey key, String algorithm) throws Exception { 136 137 byte[] result = indata; 138 139 Cipher c = Cipher.getInstance(algorithm); 140 c.init(Cipher.ENCRYPT_MODE, key); 141 algorithmParameters = c.getParameters(); 142 143 byte[] r1 = c.update(result); 144 byte[] r2 = c.doFinal(); 145 146 result = new byte[r1.length + r2.length]; 147 System.arraycopy(r1, 0, result, 0, r1.length); 148 System.arraycopy(r2, 0, result, r1.length, r2.length); 149 150 return result; 151 } 152 153 private byte[] decrypt(byte[] indata, SecretKey key, String algorithm) throws Exception { 154 155 byte[] result = indata; 156 157 Cipher c = Cipher.getInstance(algorithm); 158 c.init(Cipher.DECRYPT_MODE, key, algorithmParameters); 159 160 byte[] r1 = c.update(result); 161 byte[] r2 = c.doFinal(); 162 163 result = new byte[r1.length + r2.length]; 164 System.arraycopy(r1, 0, result, 0, r1.length); 165 System.arraycopy(r2, 0, result, r1.length, r2.length); 166 return result; 167 } 168 169 private static byte[] readClassfile16(Class<? extends HotSpotCryptoSubstitutionTest> c) throws IOException { 170 String classFilePath = "/" + c.getName().replace('.', '/') + ".class"; 171 InputStream stream = c.getResourceAsStream(classFilePath); 172 int bytesToRead = stream.available(); 173 bytesToRead -= bytesToRead % 16; 174 byte[] classFile = new byte[bytesToRead]; 175 new DataInputStream(stream).readFully(classFile); 176 return classFile; 177 } 178 179 public byte[] runEncryptDecrypt(SecretKey key, String algorithm) throws Exception { 180 byte[] indata = input.clone(); 181 byte[] cipher = encrypt(indata, key, algorithm); 182 byte[] plain = decrypt(cipher, key, algorithm); 183 Assert.assertArrayEquals(indata, plain); 184 return plain; 185 } 186 }