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;
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 {
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) {
if (result[1]) {
String supportedMechs = getMechList();
debug("Prov: supported mechs = " + supportedMechs);
StringTokenizer st = new StringTokenizer(supportedMechs, ":,;");
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();
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) {
debug("Skip Unrecognized Algorithm: " + mechName);
}
}
provProp.put("AlgorithmParameters.GCM",
sd("AlgorithmParameters", "GCM",
"com.oracle.security.ucrypto.GCMParameters"));
}
if (result[0]) {
for (LibMDMech m : LibMDMech.values()) {
ServiceDesc[] services = m.getServiceDescriptions();
for (ServiceDesc entry : services) {
String sKey = entry.getType() + "." + entry.getAlgorithm();
provProp.putIfAbsent(sKey, entry);
}
}
};
} else {
debug("Prov: unexpected ucrypto library loading error, got " + result.length);
}
}
} catch (AccessControlException ace) {
if (DEBUG) ace.printStackTrace();
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);
}
}