1 /*
   2  * Copyright (c) 2014, 2020, 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.oracle.security.ucrypto;
  27 
  28 import java.io.IOException;
  29 import java.io.File;
  30 import java.lang.reflect.Constructor;
  31 import java.util.*;
  32 import java.security.*;
  33 import static sun.security.util.SecurityConstants.PROVIDER_VER;
  34 
  35 /**
  36  * OracleUcrypto provider main class.
  37  *
  38  * @since 9
  39  */
  40 public final class UcryptoProvider extends Provider {
  41 
  42     private static final long serialVersionUID = 351251234302833L;
  43 
  44     private static boolean DEBUG = false;
  45     private static HashMap<String, ServiceDesc> provProp = null;
  46     private static String defConfigName = "";
  47 
  48     static {
  49         try {
  50             // cannot use LoadLibraryAction because that would make the native
  51             // library available to the bootclassloader, but we run in the
  52             // platform classloader.
  53             provProp = AccessController.doPrivileged
  54                 (new PrivilegedAction<>() {
  55                     @Override
  56                     public HashMap<String, ServiceDesc> run() {
  57                         String osname = System.getProperty("os.name");
  58                         if (osname.startsWith("SunOS")) {
  59                             try {
  60                                 DEBUG = Boolean.parseBoolean(System.getProperty("com.oracle.security.ucrypto.debug"));
  61                                 String javaHome = System.getProperty("java.home");
  62                                 String sep = System.getProperty("file.separator");
  63                                 defConfigName = javaHome + sep + "conf" + sep + "security" + sep +
  64                                     "ucrypto-solaris.cfg";
  65                                 System.loadLibrary("j2ucrypto");
  66                                 return new HashMap<>();
  67                             } catch (Error err) {
  68                                 if (DEBUG) err.printStackTrace();
  69                             } catch (SecurityException se) {
  70                                 if (DEBUG) se.printStackTrace();
  71                             }
  72                         }
  73                         return null;
  74                     }
  75                 });
  76             if (provProp != null) {
  77                 boolean[] result = loadLibraries();
  78                 if (result.length == 2) {
  79                     // true when libsoftcrypto or libucrypto(S12) has been successfully loaded
  80                     if (result[1]) {
  81                         String supportedMechs = getMechList();
  82                         debug("Prov: supported mechs = " + supportedMechs);
  83                         StringTokenizer st = new StringTokenizer(supportedMechs, ":,;");
  84                         // format: numOfSupportedMechs:[mechName,mechValue;]+
  85                         // skip the first one which is numberOfSupportedMechs
  86                         st.nextToken();
  87                         while (st.hasMoreTokens()) {
  88                             String mechName = st.nextToken();
  89                             int nativeMechVal = Integer.parseInt(st.nextToken());
  90                             try {
  91                                 UcryptoMech m = Enum.valueOf(UcryptoMech.class, mechName);
  92                                 m.setValue(nativeMechVal);
  93                                 ServiceDesc[] services = m.getServiceDescriptions();
  94                                 // defined in UcryptoMech as unsupported
  95                                 if (services == null || services.length == 0) {
  96                                     debug("Skip Unsupported Algorithm: " + mechName);
  97                                     continue;
  98                                 }
  99                                 for (int p = 0; p < services.length; p++) {
 100                                     ServiceDesc entry = services[p];
 101                                     provProp.put(entry.getType() + "." + entry.getAlgorithm(),
 102                                                  entry);
 103                                 }
 104                             } catch (IllegalArgumentException iae) {
 105                                 // not defined in UcryptoMech
 106                                 debug("Skip Unrecognized Algorithm: " + mechName);
 107                             }
 108                         }
 109                         // NOTE: GCM support is only available since jdk 7
 110                         provProp.put("AlgorithmParameters.GCM",
 111                                      sd("AlgorithmParameters", "GCM",
 112                                         "com.oracle.security.ucrypto.GCMParameters"));
 113                     }
 114                     // true when libmd is needed and has been successfully loaded
 115                     if (result[0]) {
 116                         for (LibMDMech m : LibMDMech.values()) {
 117                             ServiceDesc[] services = m.getServiceDescriptions();
 118                             for (ServiceDesc entry : services) {
 119                                 String sKey = entry.getType() + "." + entry.getAlgorithm();
 120                                 //  only register if none has been registered
 121                                 provProp.putIfAbsent(sKey, entry);
 122                             }
 123                         }
 124                     };
 125                 } else {
 126                     debug("Prov: unexpected ucrypto library loading error, got " + result.length);
 127                 }
 128             }
 129         } catch (AccessControlException ace) {
 130             if (DEBUG) ace.printStackTrace();
 131             // disable Ucrypto provider
 132             provProp = null;
 133         }
 134     }
 135 
 136     private static ServiceDesc sd(String type, String algo, String cn) {
 137         return new ServiceDesc(type, algo, cn, null);
 138     }
 139 
 140     private static final class ProviderService extends Provider.Service {
 141         ProviderService(Provider p, ServiceDesc sd) {
 142             super(p, sd.getType(), sd.getAlgorithm(), sd.getClassName(),
 143                   sd.getAliases(), null);
 144         }
 145 
 146         @SuppressWarnings("deprecation")
 147         @Override
 148         public Object newInstance(Object ctrParamObj)
 149             throws NoSuchAlgorithmException {
 150             String type = getType();
 151             if (ctrParamObj != null) {
 152                 throw new InvalidParameterException
 153                     ("constructorParameter not used with " + type + " engines");
 154             }
 155             String algo = getAlgorithm();
 156             try {
 157                 if (type.equals("Cipher")) {
 158                     int keySize = -1;
 159                     if (algo.charAt(3) == '_') {
 160                         keySize = Integer.parseInt(algo.substring(4, 7))/8;
 161                     }
 162                     String implClass = getClassName();
 163                     Class<?> clz = Class.forName(implClass);
 164                     if (keySize != -1) {
 165                         Constructor<?> ctr = clz.getConstructor(int.class);
 166                         return ctr.newInstance(keySize);
 167                     } else {
 168                         return clz.newInstance();
 169                     }
 170                 } else if (type.equals("Signature") || type.equals("MessageDigest")) {
 171                     String implClass = getClassName();
 172                     Class<?> clz = Class.forName(implClass);
 173                     return clz.newInstance();
 174                 } else if (type.equals("AlgorithmParameters")) {
 175                     if (algo.equals("GCM")) {
 176                         return new GCMParameters();
 177                     }
 178                 }
 179             } catch (Exception ex) {
 180                 throw new NoSuchAlgorithmException("Error constructing " +
 181                     type + " for " + algo + " using OracleUcrypto", ex);
 182             }
 183             throw new ProviderException("No impl for " + algo +
 184                 " " + type);
 185         }
 186     }
 187 
 188     static Provider provider = null;
 189     private static native boolean[] loadLibraries();
 190     private static native String getMechList();
 191 
 192     static void debug(String msg) {
 193         if (DEBUG) {
 194             System.out.println("UCrypto/" + msg);
 195         }
 196     }
 197 
 198     public UcryptoProvider() {
 199         super("OracleUcrypto", PROVIDER_VER, "Provider using Oracle Ucrypto API");
 200 
 201         AccessController.doPrivileged(new PrivilegedAction<>() {
 202             public Void run() {
 203                 init(defConfigName);
 204                 return null;
 205             }
 206         });
 207         if (provider == null) provider = this;
 208     }
 209 
 210     private void init(final String configName) {
 211         if (provProp != null) {
 212             debug("Prov: configuration file " + configName);
 213             Config c;
 214             try {
 215                 c = new Config(configName);
 216             } catch (Exception ex) {
 217                 throw new UcryptoException("Error parsing Config", ex);
 218             }
 219 
 220             String[] disabledServices = c.getDisabledServices();
 221             for (String ds : disabledServices) {
 222                 if (provProp.remove(ds) != null) {
 223                     debug("Prov: remove config-disabled service " + ds);
 224                 } else {
 225                     debug("Prov: ignore unsupported service " + ds);
 226                 }
 227             }
 228 
 229             for (ServiceDesc s: provProp.values()) {
 230                 debug("Prov: add service for " + s);
 231                 putService(new ProviderService(this, s));
 232             }
 233         }
 234     }
 235 
 236     @Override
 237     public Provider configure(String configArg) throws InvalidParameterException {
 238         try {
 239             init(configArg);
 240         } catch (UcryptoException ue) {
 241             InvalidParameterException ipe =
 242                     new InvalidParameterException("Error using " + configArg);
 243             ipe.initCause(ue.getCause());
 244             throw ipe;
 245         }
 246         return this;
 247     }
 248 
 249     public boolean equals(Object obj) {
 250         return this == obj;
 251     }
 252 
 253     public int hashCode() {
 254         return System.identityHashCode(this);
 255     }
 256 }