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