Uploaded image for project: 'WildFly Elytron'
  1. WildFly Elytron
  2. ELY-2064

Elytron Quarkus integration not supporting key cloning

XMLWordPrintable

    • Icon: Enhancement Enhancement
    • Resolution: Unresolved
    • Icon: Critical Critical
    • None
    • None
    • None
    • None
    • Undefined

      Elytron Quarkus integration does not support key cloning, which causes Infinispan native server to not be buildable. See here for details.

      From an Elytron perspective, a way to solve this would be to have a substitution that looks something like this, where MethodHandle uses are replaced by standard reflection: 

      package org.example.elytron.graal;
      
      import com.oracle.svm.core.annotate.Substitute;
      import com.oracle.svm.core.annotate.TargetClass;
      
      import java.lang.reflect.Constructor;
      import java.lang.reflect.Method;
      import java.lang.reflect.UndeclaredThrowableException;
      import java.security.Key;
      import java.security.PrivilegedAction;
      import java.util.function.UnaryOperator;
      
      import static java.security.AccessController.doPrivileged;
      
      @TargetClass(className = "org.wildfly.security.key.KeyUtil$KeyClonerCreator")
      final class Target_org_wildfly_security_key_KeyUtil_KeyClonerCreator
      {
          @Substitute
          private UnaryOperator<Key> checkForCloneMethod(final Class<?> declType, final Class<?> returnType)
          {
              System.out.printf("Call checkForCloneMethod(%s,%s)%n", declType, returnType);
              final Method method = doPrivileged(new PrivilegedAction<Method>()
              {
                  @Override
                  public Method run()
                  {
                      try
                      {
                          final var cloneMethod = declType.getDeclaredMethod("clone");
                          if (cloneMethod.getReturnType() == returnType)
                              return cloneMethod;
      
                          return null;
                      }
                      catch (NoSuchMethodException e)
                      {
                          return null;
                      }
                  }
              });
      
              if (method == null)
                  return null;
      
              return new UnaryOperator<Key>()
              {
                  @Override
                  public Key apply(Key key)
                  {
                      try
                      {
                          return (Key) method.invoke(key);
                      }
                      catch (RuntimeException | Error e)
                      {
                          throw e;
                      }
                      catch (Throwable throwable)
                      {
                          throw new UndeclaredThrowableException(throwable);
                      }
                  }
              };
          }
      
          @Substitute
          private UnaryOperator<Key> checkForCopyCtor(final Class<?> declType, final Class<?> paramType)
          {
              System.out.printf("Call checkForCopyCtor(%s,%s)%n", declType, paramType);
              final Constructor<?> constructor = doPrivileged(new PrivilegedAction<Constructor<?>>()
              {
                  @Override
                  public Constructor<?> run()
                  {
                      try
                      {
                          return declType.getDeclaredConstructor(paramType);
                      }
                      catch (NoSuchMethodException e)
                      {
                          System.out.printf("Copy ctor in %s for parameter %s not found%n", declType, paramType);
                          return null;
                      }
                  }
              });
      
              if (constructor == null)
                  return null;
      
              return new UnaryOperator<Key>()
              {
                  @Override
                  public Key apply(Key key)
                  {
                      try
                      {
                          return (Key) constructor.newInstance(key);
                      }
                      catch (RuntimeException | Error e)
                      {
                          throw e;
                      }
                      catch (Throwable throwable)
                      {
                          throw new UndeclaredThrowableException(throwable);
                      }
                  }
              };
          }
      } 

      These substitutions alone are not enough, there are also needs to be some reflection registrations for the keys for which this is expected to work. As example:

      package org.example.elytron.graal;
      
      import com.oracle.svm.core.annotate.AutomaticFeature;
      import org.example.elytron.Main;
      import org.graalvm.nativeimage.hosted.Feature;
      import org.graalvm.nativeimage.hosted.RuntimeReflection;
      
      import javax.crypto.SecretKey;
      import java.lang.reflect.UndeclaredThrowableException;
      
      @AutomaticFeature
      public class RuntimeReflectionRegistrations implements Feature
      {
          public void beforeAnalysis(BeforeAnalysisAccess access)
          {
              try
              {
                  RuntimeReflection.register(Main.CopyConstructorSecretKey.class.getDeclaredConstructor(SecretKey.class));
                  RuntimeReflection.register(Main.CopyConstructorSecretKey.class.getDeclaredMethod("destroy"));
                  RuntimeReflection.register(Main.CloneMethodSecretKey.class.getDeclaredMethod("clone"));
                  RuntimeReflection.register(Main.CloneMethodSecretKey.class.getDeclaredMethod("destroy"));
              }
              catch (NoSuchMethodException e)
              {
                  throw new UndeclaredThrowableException(e);
              }
          }
      } 

      This main class exercises these substitutions which have been verified to work with a couple of custom designed secret keys that fall within the expected substitution.

      The Elytron and Infinispan teams should work together to figure out which keys require support from the reflection calls above.

      All the code above can be found in this sample project. 

            Unassigned Unassigned
            rh-ee-galder Galder Zamarreño
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated: