# HG changeset patch # User Doug Simon # Date 1368801916 -7200 # Node ID a2074a73aeefccd4592d6ea2b79a81ca7f325d94 # Parent 43a94291d239072abb7ee6a216848bd68a504534 added test for AES intrinsification diff -r 43a94291d239 -r a2074a73aeef graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/HotSpotCryptoSubstitutionTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/HotSpotCryptoSubstitutionTest.java Fri May 17 16:45:16 2013 +0200 @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2013, 2013, 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.oracle.graal.hotspot.test; + +import static com.oracle.graal.hotspot.HotSpotGraalRuntime.*; + +import java.io.*; +import java.lang.reflect.*; +import java.security.*; + +import javax.crypto.*; + +import org.junit.*; + +import com.oracle.graal.api.code.*; +import com.oracle.graal.api.meta.*; +import com.oracle.graal.compiler.test.*; +import com.oracle.graal.debug.*; +import com.oracle.graal.hotspot.*; +import com.oracle.graal.hotspot.bridge.CompilerToVM.CodeInstallResult; +import com.oracle.graal.hotspot.meta.*; +import com.oracle.graal.nodes.*; + +/** + * Tests the intrinsicification of certain crypto methods. + */ +public class HotSpotCryptoSubstitutionTest extends GraalCompilerTest { + + @Override + protected InstalledCode addMethod(ResolvedJavaMethod method, CompilationResult compResult, StructuredGraph graph) { + HotSpotResolvedJavaMethod hsMethod = (HotSpotResolvedJavaMethod) method; + HotSpotNmethod installedCode = new HotSpotNmethod(hsMethod, graph, true); + HotSpotCompiledNmethod compiledNmethod = new HotSpotCompiledNmethod(hsMethod, StructuredGraph.INVOCATION_ENTRY_BCI, compResult); + CodeInstallResult result = graalRuntime().getCompilerToVM().installCode(compiledNmethod, installedCode, null); + Assert.assertEquals("Error installing method " + method + ": " + result, result, CodeInstallResult.OK); + if (Debug.isDumpEnabled()) { + Debug.dump(new Object[]{compResult, installedCode}, "After code installation"); + } + + // HotSpotRuntime hsRuntime = (HotSpotRuntime) runtime; + // TTY.println(hsMethod.toString()); + // TTY.println(hsRuntime.disassemble(installedCode)); + return installedCode; + } + + @Test + public void testAESEncryptSubstitution() throws Exception { + byte[] seed = {0x4, 0x7, 0x1, 0x1}; + SecureRandom random = new SecureRandom(seed); + KeyGenerator aesKeyGen = KeyGenerator.getInstance("AES"); + aesKeyGen.init(128, random); + SecretKey aesKey = aesKeyGen.generateKey(); + byte[] input = readClassfile16(getClass()); + + ByteArrayOutputStream expected = new ByteArrayOutputStream(); + expected.write(runEncryptDecrypt(aesKey, "AES/CBC/NoPadding", input)); + expected.write(runEncryptDecrypt(aesKey, "AES/CBC/PKCS5Padding", input)); + + if (compiledAndInstall("com.sun.crypto.provider.AESCrypt", "encryptBlock", "decryptBlock")) { + ByteArrayOutputStream actual = new ByteArrayOutputStream(); + actual.write(runEncryptDecrypt(aesKey, "AES/CBC/NoPadding", input)); + actual.write(runEncryptDecrypt(aesKey, "AES/CBC/PKCS5Padding", input)); + Assert.assertArrayEquals(expected.toByteArray(), actual.toByteArray()); + } + + if (compiledAndInstall("com.sun.crypto.provider.CipherBlockChaining", "encrypt", "decrypt")) { + ByteArrayOutputStream actual = new ByteArrayOutputStream(); + actual.write(runEncryptDecrypt(aesKey, "AES/CBC/NoPadding", input)); + actual.write(runEncryptDecrypt(aesKey, "AES/CBC/PKCS5Padding", input)); + Assert.assertArrayEquals(expected.toByteArray(), actual.toByteArray()); + } + } + + /** + * Compiles and installs the substitution for some specified methods. Once installed, the next + * execution of the methods will use the newly installed code. + * + * @param className the name of the class for which substitutions are available + * @param methodNames the names of the substituted methods + * @return true if at least one substitution was compiled and installed + */ + private boolean compiledAndInstall(String className, String... methodNames) { + boolean atLeastOneCompiled = false; + for (String methodName : methodNames) { + Method method = lookup(className, methodName); + if (method != null) { + ResolvedJavaMethod installedCodeOwner = runtime.lookupJavaMethod(method); + StructuredGraph graph = replacements.getMethodSubstitution(installedCodeOwner); + if (graph != null) { + Assert.assertNotNull(getCode(installedCodeOwner, graph, true)); + atLeastOneCompiled = true; + } else { + Assert.assertFalse(graalRuntime().getConfig().useAESIntrinsics); + } + } + } + return atLeastOneCompiled; + } + + private static Method lookup(String className, String methodName) { + Class c; + try { + c = Class.forName(className); + for (Method m : c.getDeclaredMethods()) { + if (m.getName().equals(methodName)) { + return m; + } + } + // If the expected security provider exists, the specific method should also exist + throw new NoSuchMethodError(className + "." + methodName); + } catch (ClassNotFoundException e) { + // It's ok to not find the class - a different security provider + // may have been installed + return null; + } + } + + AlgorithmParameters algorithmParameters; + + private byte[] encrypt(byte[] indata, SecretKey key, String algorithm) throws Exception { + + byte[] result = indata; + + Cipher c = Cipher.getInstance(algorithm); + c.init(Cipher.ENCRYPT_MODE, key); + algorithmParameters = c.getParameters(); + + byte[] r1 = c.update(result); + byte[] r2 = c.doFinal(); + + result = new byte[r1.length + r2.length]; + System.arraycopy(r1, 0, result, 0, r1.length); + System.arraycopy(r2, 0, result, r1.length, r2.length); + + return result; + } + + private byte[] decrypt(byte[] indata, SecretKey key, String algorithm) throws Exception { + + byte[] result = indata; + + Cipher c = Cipher.getInstance(algorithm); + c.init(Cipher.DECRYPT_MODE, key, algorithmParameters); + + byte[] r1 = c.update(result); + byte[] r2 = c.doFinal(); + + result = new byte[r1.length + r2.length]; + System.arraycopy(r1, 0, result, 0, r1.length); + System.arraycopy(r2, 0, result, r1.length, r2.length); + return result; + } + + private static byte[] readClassfile16(Class c) throws IOException { + String classFilePath = "/" + c.getName().replace('.', '/') + ".class"; + InputStream stream = c.getResourceAsStream(classFilePath); + int bytesToRead = stream.available(); + bytesToRead -= bytesToRead % 16; + byte[] classFile = new byte[bytesToRead]; + new DataInputStream(stream).readFully(classFile); + return classFile; + } + + public byte[] runEncryptDecrypt(SecretKey key, String algorithm, byte[] input) throws Exception { + byte[] indata = input.clone(); + byte[] cipher = encrypt(indata, key, algorithm); + byte[] plain = decrypt(cipher, key, algorithm); + Assert.assertArrayEquals(indata, plain); + return plain; + } +} diff -r 43a94291d239 -r a2074a73aeef graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/CipherBlockChainingSubstitutions.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/CipherBlockChainingSubstitutions.java Fri May 17 13:55:35 2013 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/CipherBlockChainingSubstitutions.java Fri May 17 16:45:16 2013 +0200 @@ -72,6 +72,16 @@ } } + @MethodSubstitution(isStatic = false) + static void decrypt(Object rcvr, byte[] in, int inOffset, int inLength, byte[] out, int outOffset) { + Object embeddedCipher = Word.fromObject(rcvr).readObject(Word.unsigned(embeddedCipherOffset), ANY_LOCATION); + if (in != out && getAESCryptClass().isInstance(embeddedCipher)) { + crypt(rcvr, in, inOffset, inLength, out, outOffset, embeddedCipher, false); + } else { + decrypt(rcvr, in, inOffset, inLength, out, outOffset); + } + } + private static void crypt(Object rcvr, byte[] in, int inOffset, int inLength, byte[] out, int outOffset, Object embeddedCipher, boolean encrypt) { Word kAddr = Word.fromObject(embeddedCipher).readWord(Word.unsigned(AESCryptSubstitutions.kOffset), ANY_LOCATION).add(arrayBaseOffset(Kind.Byte)); Word rAddr = Word.unsigned(GetObjectAddressNode.get(rcvr)).readWord(Word.unsigned(rOffset), ANY_LOCATION).add(arrayBaseOffset(Kind.Byte)); @@ -85,16 +95,6 @@ } - @MethodSubstitution(isStatic = false) - static void decrypt(Object rcvr, byte[] in, int inOffset, int inLength, byte[] out, int outOffset) { - Object embeddedCipher = Word.fromObject(rcvr).readObject(Word.unsigned(embeddedCipherOffset), ANY_LOCATION); - if (in != out && getAESCryptClass().isInstance(embeddedCipher)) { - crypt(rcvr, in, inOffset, inLength, out, outOffset, embeddedCipher, false); - } else { - decrypt(rcvr, in, inOffset, inLength, out, outOffset); - } - } - abstract static class AESCryptStubCall extends DeoptimizingStubCall implements LIRGenLowerable { @Input private ValueNode in;