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
26package com.oracle.security.ucrypto;
27
28import java.io.IOException;
29import java.io.File;
30import java.lang.reflect.Constructor;
31import java.util.*;
32import java.security.*;
33import static sun.security.util.SecurityConstants.PROVIDER_VER;
34
35
36/**
37 * OracleUcrypto provider main class.
38 *
39 * @since 9
40 */
41public 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}
259