src/jdk.crypto.ucrypto/solaris/classes/com/oracle/security/ucrypto/UcryptoProvider.java
author ascarpino
Fri, 20 Jul 2018 09:55:15 -0700
changeset 51216 e429a304c97d
parent 47216 71c04702a3d5
permissions -rw-r--r--
8204196: integer cleanup Reviewed-by: xuelei

/*
 * Copyright (c) 2014, 2016, 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.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * 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.security.ucrypto;

import java.io.IOException;
import java.io.File;
import java.lang.reflect.Constructor;
import java.util.*;
import java.security.*;
import static sun.security.util.SecurityConstants.PROVIDER_VER;


/**
 * OracleUcrypto provider main class.
 *
 * @since 9
 */
public final class UcryptoProvider extends Provider {

    private static final long serialVersionUID = 351251234302833L;

    private static boolean DEBUG = false;
    private static HashMap<String, ServiceDesc> provProp = null;
    private static String defConfigName = "";

    static {
        try {
            // cannot use LoadLibraryAction because that would make the native
            // library available to the bootclassloader, but we run in the
            // platform classloader.
            provProp = AccessController.doPrivileged
                (new PrivilegedAction<>() {
                    @Override
                    public HashMap<String, ServiceDesc> run() {
                        String osname = System.getProperty("os.name");
                        if (osname.startsWith("SunOS")) {
                            try {
                                DEBUG = Boolean.parseBoolean(System.getProperty("com.oracle.security.ucrypto.debug"));
                                String javaHome = System.getProperty("java.home");
                                String sep = System.getProperty("file.separator");
                                defConfigName = javaHome + sep + "conf" + sep + "security" + sep +
                                    "ucrypto-solaris.cfg";
                                System.loadLibrary("j2ucrypto");
                                return new HashMap<>();
                            } catch (Error err) {
                                if (DEBUG) err.printStackTrace();
                            } catch (SecurityException se) {
                                if (DEBUG) se.printStackTrace();
                            }
                        }
                        return null;
                    }
                });
            if (provProp != null) {
                boolean[] result = loadLibraries();
                if (result.length == 2) {
                    // true when libsoftcrypto or libucrypto(S12) has been successfully loaded
                    if (result[1]) {
                        String supportedMechs = getMechList();
                        debug("Prov: supported mechs = " + supportedMechs);
                        StringTokenizer st = new StringTokenizer(supportedMechs, ":,;");
                        // format: numOfSupportedMechs:[mechName,mechValue;]+
                        // skip the first one which is numberOfSupportedMechs
                        st.nextToken();
                        while (st.hasMoreTokens()) {
                            String mechName = st.nextToken();
                            int nativeMechVal = Integer.parseInt(st.nextToken());
                            try {
                                UcryptoMech m = Enum.valueOf(UcryptoMech.class, mechName);
                                m.setValue(nativeMechVal);
                                ServiceDesc[] services = m.getServiceDescriptions();
                                // defined in UcryptoMech as unsupported
                                if (services == null || services.length == 0) {
                                    debug("Skip Unsupported Algorithm: " + mechName);
                                    continue;
                                }
                                for (int p = 0; p < services.length; p++) {
                                    ServiceDesc entry = services[p];
                                    provProp.put(entry.getType() + "." + entry.getAlgorithm(),
                                                 entry);
                                }
                            } catch (IllegalArgumentException iae) {
                                // not defined in UcryptoMech
                                debug("Skip Unrecognized Algorithm: " + mechName);
                            }
                        }
                        // NOTE: GCM support is only available since jdk 7
                        provProp.put("AlgorithmParameters.GCM",
                                     sd("AlgorithmParameters", "GCM",
                                        "com.oracle.security.ucrypto.GCMParameters"));
                    }
                    // true when libmd is needed and has been successfully loaded
                    if (result[0]) {
                        for (LibMDMech m : LibMDMech.values()) {
                            ServiceDesc[] services = m.getServiceDescriptions();
                            for (ServiceDesc entry : services) {
                                String sKey = entry.getType() + "." + entry.getAlgorithm();
                                //  only register if none has been registered
                                provProp.putIfAbsent(sKey, entry);
                            }
                        }
                    };
                } else {
                    debug("Prov: unexpected ucrypto library loading error, got " + result.length);
                }
            }
        } catch (AccessControlException ace) {
            if (DEBUG) ace.printStackTrace();
            // disable Ucrypto provider
            provProp = null;
        }
    }

    private static ServiceDesc sd(String type, String algo, String cn,
        String... aliases) {
        return new ServiceDesc(type, algo, cn, aliases);
    }

    private static final class ProviderService extends Provider.Service {
        ProviderService(Provider p, ServiceDesc sd) {
            super(p, sd.getType(), sd.getAlgorithm(), sd.getClassName(),
                  sd.getAliases(), null);
        }

        @SuppressWarnings("deprecation")
        @Override
        public Object newInstance(Object ctrParamObj)
            throws NoSuchAlgorithmException {
            String type = getType();
            if (ctrParamObj != null) {
                throw new InvalidParameterException
                    ("constructorParameter not used with " + type + " engines");
            }
            String algo = getAlgorithm();
            try {
                if (type.equals("Cipher")) {
                    int keySize = -1;
                    if (algo.charAt(3) == '_') {
                        keySize = Integer.parseInt(algo.substring(4, 7))/8;
                    }
                    String implClass = getClassName();
                    Class<?> clz = Class.forName(implClass);
                    if (keySize != -1) {
                        Constructor<?> ctr = clz.getConstructor(int.class);
                        return ctr.newInstance(keySize);
                    } else {
                        return clz.newInstance();
                    }
                } else if (type.equals("Signature") || type.equals("MessageDigest")) {
                    String implClass = getClassName();
                    Class<?> clz = Class.forName(implClass);
                    return clz.newInstance();
                } else if (type.equals("AlgorithmParameters")) {
                    if (algo.equals("GCM")) {
                        return new GCMParameters();
                    }
                }
            } catch (Exception ex) {
                throw new NoSuchAlgorithmException("Error constructing " +
                    type + " for " + algo + " using OracleUcrypto", ex);
            }
            throw new ProviderException("No impl for " + algo +
                " " + type);
        }
    }

    static Provider provider = null;
    private static native boolean[] loadLibraries();
    private static native String getMechList();

    static void debug(String msg) {
        if (DEBUG) {
            System.out.println("UCrypto/" + msg);
        }
    }

    public UcryptoProvider() {
        super("OracleUcrypto", PROVIDER_VER, "Provider using Oracle Ucrypto API");

        AccessController.doPrivileged(new PrivilegedAction<>() {
            public Void run() {
                init(defConfigName);
                return null;
            }
        });
        if (provider == null) provider = this;
    }

    private void init(final String configName) {
        if (provProp != null) {
            debug("Prov: configuration file " + configName);
            Config c;
            try {
                c = new Config(configName);
            } catch (Exception ex) {
                throw new UcryptoException("Error parsing Config", ex);
            }

            String[] disabledServices = c.getDisabledServices();
            for (String ds : disabledServices) {
                if (provProp.remove(ds) != null) {
                    debug("Prov: remove config-disabled service " + ds);
                } else {
                    debug("Prov: ignore unsupported service " + ds);
                }
            }

            for (ServiceDesc s: provProp.values()) {
                debug("Prov: add service for " + s);
                putService(new ProviderService(this, s));
            }
        }
    }

    @Override
    public Provider configure(String configArg) throws InvalidParameterException {
        try {
            init(configArg);
        } catch (UcryptoException ue) {
            InvalidParameterException ipe =
                    new InvalidParameterException("Error using " + configArg);
            ipe.initCause(ue.getCause());
            throw ipe;
        }
        return this;
    }

    public boolean equals(Object obj) {
        return this == obj;
    }

    public int hashCode() {
        return System.identityHashCode(this);
    }
}